diff --git a/res/core/messages.xml b/res/core/messages.xml index dfbeecd07..e65d89d1e 100644 --- a/res/core/messages.xml +++ b/res/core/messages.xml @@ -7112,6 +7112,15 @@ "$unit($target) receives $int($amount) $resource($resource,$amount) from $unit($unit)." + + + + + + "$unit($unit) ertränkt $int($amount) Person$if($eq($amount,1),"","en")." + "$unit($unit) drowns $int($amount)." + + diff --git a/scripts/eressea/eternath.lua b/scripts/eressea/eternath.lua index 1a93ad352..4c230e733 100644 --- a/scripts/eressea/eternath.lua +++ b/scripts/eressea/eternath.lua @@ -23,6 +23,7 @@ local eternath = {} function eternath.update() if b1 and b2 then + local size = 5 local units1 = gates.units(b1, size) local units2 = gates.units(b2, size) diff --git a/scripts/eressea/gates.lua b/scripts/eressea/gates.lua index 2fb6736ba..c6ce0d7bc 100644 --- a/scripts/eressea/gates.lua +++ b/scripts/eressea/gates.lua @@ -13,7 +13,7 @@ function gates.travel(b, units) end function gates.units(b, maxsize) - local size = maxsize + local size = maxsize or 1 local units = {} local u diff --git a/src/creport.c b/src/creport.c index 5f9700533..9829f7e7c 100644 --- a/src/creport.c +++ b/src/creport.c @@ -1523,7 +1523,7 @@ report_computer(const char *filename, report_context * ctx, const char *charset) fprintf(F, "%d;Basis\n", 36); fprintf(F, "%d;Runde\n", turn); fprintf(F, "%d;Zeitalter\n", era); - fprintf(F, "%d;Build\n", VERSION_BUILD); + fprintf(F, "%d.%d.%d;Build\n", VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD); if (mailto != NULL) { fprintf(F, "\"%s\";mailto\n", mailto); fprintf(F, "\"%s\";mailcmd\n", locale_string(f->locale, "mailcmd")); diff --git a/src/give.c b/src/give.c index ac928ab9b..8887018ba 100644 --- a/src/give.c +++ b/src/give.c @@ -210,63 +210,81 @@ struct order *ord) return 0; } -void give_men(int n, unit * u, unit * u2, struct order *ord) +static bool can_give_men(const unit *u, order *ord, message **msg) { + if (u_race(u) == get_race(RC_SNOTLING)) { + /* snotlings may not be given to the peasants. */ + if (msg) *msg = msg_error(u, ord, 307); + } + else if (unit_has_cursed_item(u)) { + if (msg) *msg = msg_error(u, ord, 78); + } + else if (has_skill(u, SK_MAGIC)) { + /* cannot give units to and from magicians */ + if (msg) *msg = msg_error(u, ord, 158); + } + else if (fval(u, UFL_HUNGER)) { + /* hungry people cannot be given away */ + if (msg) *msg = msg_error(u, ord, 73); + } + else if (fval(u, UFL_LOCKED) || is_cursed(u->attribs, C_SLAVE, 0)) { + if (msg) *msg = msg_error(u, ord, 74); + } + else { + return true; + } + return false; +} + +message * give_men(int n, unit * u, unit * u2, struct order *ord) { ship *sh; int k = 0; int error = 0; + message * msg; - if (u2 && u->faction != u2->faction && u->faction->age < GiveRestriction()) { - ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "giverestriction", - "turns", GiveRestriction())); - return; + assert(u2); + + if (!can_give_men(u, ord, &msg)) { + return msg; + } + + if (u->faction != u2->faction && u->faction->age < GiveRestriction()) { + return msg_feedback(u, ord, "giverestriction", + "turns", GiveRestriction()); } else if (u == u2) { error = 10; } - else if (!u2 && u_race(u) == get_race(RC_SNOTLING)) { - /* snotlings may not be given to the peasants. */ - error = 307; - } - else if (u2 && u2->number && (fval(u, UFL_HERO) != fval(u2, UFL_HERO))) { + else if (u2->number && (fval(u, UFL_HERO) != fval(u2, UFL_HERO))) { /* heroes may not be given to non-heroes and vice versa */ error = 75; } - else if (unit_has_cursed_item(u) || (u2 && unit_has_cursed_item(u2))) { + else if (unit_has_cursed_item(u2)) { error = 78; } - else if (fval(u, UFL_LOCKED) || is_cursed(u->attribs, C_SLAVE, 0)) { - error = 74; - } - else if (u2 && fval(u, UFL_HUNGER)) { - /* hungry people cannot be given away */ - error = 73; - } - else if (u2 && (fval(u2, UFL_LOCKED) || is_cursed(u2->attribs, C_SLAVE, 0))) { + else if (fval(u2, UFL_LOCKED) || is_cursed(u2->attribs, C_SLAVE, 0)) { error = 75; } - else if (u2 && !ucontact(u2, u)) { - ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_no_contact", - "target", u2)); - error = -1; + else if (!ucontact(u2, u)) { + return msg_feedback(u, ord, "feedback_no_contact", + "target", u2); } - else if (u2 && (has_skill(u, SK_MAGIC) || has_skill(u2, SK_MAGIC))) { + else if (has_skill(u2, SK_MAGIC)) { /* cannot give units to and from magicians */ error = 158; } - else if (u2 && (fval(u, UFL_WERE) != fval(u2, UFL_WERE))) { + else if (fval(u, UFL_WERE) != fval(u2, UFL_WERE)) { /* werewolves can't be given to non-werewolves and vice-versa */ error = 312; } - else if (u2 && u2->number != 0 && u_race(u2) != u_race(u)) { + else if (u2->number != 0 && u_race(u2) != u_race(u)) { log_debug("faction %s attempts to give %s to %s.\n", itoa36(u->faction->no), u_race(u)->_name, u_race(u2)->_name); error = 139; } - else if (u2 != NULL && (get_racename(u2->attribs) - || get_racename(u->attribs))) { + else if (get_racename(u2->attribs) || get_racename(u->attribs)) { error = 139; } - else if (u2 && u2->faction != u->faction && !rule_transfermen()) { + else if (u2->faction != u->faction && !rule_transfermen()) { error = 74; } else { @@ -281,7 +299,7 @@ void give_men(int n, unit * u, unit * u2, struct order *ord) if (n == 0) { error = 96; } - else if (u2 && u->faction != u2->faction) { + else if (u->faction != u2->faction) { if (u2->faction->newbies + n > MAXNEWBIES) { error = 129; } @@ -303,7 +321,7 @@ void give_men(int n, unit * u, unit * u2, struct order *ord) } } - if (u2 && (has_skill(u, SK_ALCHEMY) || has_skill(u2, SK_ALCHEMY))) { + if (has_skill(u, SK_ALCHEMY) || has_skill(u2, SK_ALCHEMY)) { k = count_skill(u2->faction, SK_ALCHEMY); /* Falls die Zieleinheit keine Alchemisten sind, werden sie nun @@ -327,7 +345,7 @@ void give_men(int n, unit * u, unit * u2, struct order *ord) } if (error == 0) { - if (u2 && u2->number == 0) { + if (u2->number == 0) { set_racename(&u2->attribs, get_racename(u->attribs)); u_setrace(u2, u_race(u)); u2->irace = u->irace; @@ -337,48 +355,48 @@ void give_men(int n, unit * u, unit * u2, struct order *ord) freset(u2, UFL_HERO); } - if (u2) { - /* Einheiten von Schiffen können nicht NACH in von - * Nicht-alliierten bewachten Regionen ausführen */ - sh = leftship(u); - if (sh) { - set_leftship(u2, sh); - } - transfermen(u, u2, n); - if (u->faction != u2->faction) { - u2->faction->newbies += n; - } + /* Einheiten von Schiffen können nicht NACH in von + * Nicht-alliierten bewachten Regionen ausführen */ + sh = leftship(u); + if (sh) { + set_leftship(u2, sh); } - else { - if (getunitpeasants) { -#ifdef ORCIFICATION - if (u_race(u) == get_race(RC_SNOTLING) && !fval(u->region, RF_ORCIFIED)) { - attrib *a = a_find(u->region->attribs, &at_orcification); - if (!a) - a = a_add(&u->region->attribs, a_new(&at_orcification)); - a->data.i += n; - } -#endif - transfermen(u, NULL, n); - } - else { - error = 159; - } + transfermen(u, u2, n); + if (u->faction != u2->faction) { + u2->faction->newbies += n; } } if (error > 0) { - cmistake(u, ord, error, MSG_COMMERCE); - } - else if (!u2) { - ADDMSG(&u->faction->msgs, - msg_message("give_person_peasants", "unit amount", u, n)); + return msg_error(u, ord, error); } else if (u2->faction != u->faction) { message *msg = msg_message("give_person", "unit target amount", u, u2, n); - add_message(&u->faction->msgs, msg); add_message(&u2->faction->msgs, msg); - msg_release(msg); + return msg; } + return NULL; +} + +message * disband_men(int n, unit * u, struct order *ord) { + message * msg; + + if (!can_give_men(u, ord, &msg)) { + return msg; + } + transfermen(u, NULL, n); +#ifdef ORCIFICATION + if (u_race(u) == get_race(RC_SNOTLING) && !fval(u->region, RF_ORCIFIED)) { + attrib *a = a_find(u->region->attribs, &at_orcification); + if (!a) { + a = a_add(&u->region->attribs, a_new(&at_orcification)); + } + a->data.i += n; + } +#endif + if (fval(u->region->terrain, SEA_REGION)) { + return msg_message("give_person_ocean", "unit amount", u, n); + } + return msg_message("give_person_peasants", "unit amount", u, n); } void give_unit(unit * u, unit * u2, order * ord) @@ -406,8 +424,16 @@ void give_unit(unit * u, unit * u2, order * ord) } if (u2 == NULL) { + message *msg; if (fval(r->terrain, SEA_REGION)) { - cmistake(u, ord, 152, MSG_COMMERCE); + /* TODO: why is this here, but the unit does not actually seem to lose any men? */ + msg = disband_men(u->number, u, ord); + if (msg) { + ADDMSG(&u->faction->msgs, msg); + } + else { + cmistake(u, ord, 152, MSG_COMMERCE); + } } else if (getunitpeasants) { unit *u3; @@ -428,8 +454,13 @@ void give_unit(unit * u, unit * u2, order * ord) } } } - give_men(u->number, u, NULL, ord); - cmistake(u, ord, 153, MSG_COMMERCE); + msg = disband_men(u->number, u, ord); + if (msg) { + ADDMSG(&u->faction->msgs, msg); + } + else { + cmistake(u, ord, 153, MSG_COMMERCE); + } } else { ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found", @@ -575,12 +606,10 @@ void give_cmd(unit * u, order * ord) msg_feedback(u, ord, "race_notake", "race", u_race(u2))); return; } - if (!u2) { - if (!getunitpeasants) { - ADDMSG(&u->faction->msgs, msg_feedback(u, ord, - "feedback_unit_not_found", "")); - return; - } + if (!u2 && !getunitpeasants) { + ADDMSG(&u->faction->msgs, msg_feedback(u, ord, + "feedback_unit_not_found", "")); + return; } if (u->items) { item **itmp = &u->items; @@ -669,8 +698,11 @@ void give_cmd(unit * u, order * ord) msg_feedback(u, ord, "race_noregroup", "race", u_race(u))); } else { - n = u->number; - give_men(n, u, u2, ord); + message * msg; + msg = u2 ? give_men(u->number, u, u2, ord) : disband_men(u->number, u, ord); + if (msg) { + ADDMSG(&u->faction->msgs, msg); + } } } else if (!(u_race(u)->ec_flags & GIVEITEM) && u2 != NULL) { @@ -717,12 +749,16 @@ void give_cmd(unit * u, order * ord) } if (isparam(s, u->faction->locale, P_PERSON)) { + message * msg; if (!(u_race(u)->ec_flags & GIVEPERSON)) { ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "race_noregroup", "race", u_race(u))); return; } - give_men(n, u, u2, ord); + msg = u2 ? give_men(u->number, u, u2, ord) : disband_men(u->number, u, ord); + if (msg) { + ADDMSG(&u->faction->msgs, msg); + } return; } diff --git a/src/give.h b/src/give.h index c392ecde8..dfd53cddc 100644 --- a/src/give.h +++ b/src/give.h @@ -19,10 +19,12 @@ extern "C" { struct item_type; struct order; struct unit; + struct message; int give_item(int want, const struct item_type *itype, struct unit *src, struct unit *dest, struct order *ord); - void give_men(int n, struct unit *u, struct unit *u2, + struct message * disband_men(int n, struct unit * u, struct order *ord); + struct message * give_men(int n, struct unit *u, struct unit *u2, struct order *ord); void give_unit(struct unit *u, struct unit *u2, struct order *ord); void give_cmd(struct unit * u, struct order * ord); diff --git a/src/give.test.c b/src/give.test.c index ea5237e65..517675e4a 100644 --- a/src/give.test.c +++ b/src/give.test.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -29,12 +30,149 @@ struct give { static void setup_give(struct give *env) { struct terrain_type *ter = test_create_terrain("plain", LAND_REGION); env->r = test_create_region(0, 0, ter); - env->src = test_create_unit(env->f1, env->r); - env->dst = test_create_unit(env->f2, env->r); + env->src = env->f1 ? test_create_unit(env->f1, env->r) : 0; + env->dst = env->f2 ? test_create_unit(env->f2, env->r) : 0; env->itype = it_get_or_create(rt_get_or_create("money")); env->itype->flags |= ITF_HERB; } +static void test_give_unit_to_peasants(CuTest * tc) { + struct give env; + test_cleanup(); + env.f1 = test_create_faction(0); + env.f2 = 0; + setup_give(&env); + rsetpeasants(env.r, 0); + getunitpeasants = true; + give_unit(env.src, NULL, NULL); + CuAssertIntEquals(tc, 0, env.src->number); + CuAssertIntEquals(tc, 1, env.r->land->peasants); + test_cleanup(); +} + +static void test_give_unit_in_ocean(CuTest * tc) { + struct give env; + test_cleanup(); + env.f1 = test_create_faction(0); + env.f2 = 0; + setup_give(&env); + env.r->terrain = test_create_terrain("ocean", SEA_REGION); + getunitpeasants = true; + give_unit(env.src, NULL, NULL); + CuAssertIntEquals(tc, 0, env.src->number); + test_cleanup(); +} + +static void test_give_men(CuTest * tc) { + struct give env; + test_cleanup(); + env.f2 = env.f1 = test_create_faction(0); + setup_give(&env); + CuAssertPtrEquals(tc, 0, give_men(1, env.src, env.dst, NULL)); + CuAssertIntEquals(tc, 2, env.dst->number); + CuAssertIntEquals(tc, 0, env.src->number); + test_cleanup(); +} + +static void test_give_men_in_ocean(CuTest * tc) { + struct give env; + message * msg; + + test_cleanup(); + env.f1 = test_create_faction(0); + env.f2 = 0; + setup_give(&env); + env.r->terrain = test_create_terrain("ocean", SEA_REGION); + msg = disband_men(1, env.src, NULL); + CuAssertStrEquals(tc, "give_person_ocean", (const char *)msg->parameters[0].v); + CuAssertIntEquals(tc, 0, env.src->number); + test_cleanup(); +} + +static void test_give_men_too_many(CuTest * tc) { + struct give env; + test_cleanup(); + env.f2 = env.f1 = test_create_faction(0); + setup_give(&env); + CuAssertPtrEquals(tc, 0, give_men(2, env.src, env.dst, NULL)); + CuAssertIntEquals(tc, 2, env.dst->number); + CuAssertIntEquals(tc, 0, env.src->number); + test_cleanup(); +} + +static void test_give_men_none(CuTest * tc) { + struct give env; + message * msg; + + test_cleanup(); + env.f2 = env.f1 = test_create_faction(0); + setup_give(&env); + msg = give_men(0, env.src, env.dst, NULL); + CuAssertStrEquals(tc, "error96", (const char *)msg->parameters[3].v); + CuAssertIntEquals(tc, 1, env.dst->number); + CuAssertIntEquals(tc, 1, env.src->number); + test_cleanup(); +} + +static void test_give_men_other_faction(CuTest * tc) { + struct give env; + message * msg; + + test_cleanup(); + env.f1 = test_create_faction(0); + env.f2 = test_create_faction(0); + setup_give(&env); + usetcontact(env.dst, env.src); + msg = give_men(1, env.src, env.dst, NULL); + CuAssertStrEquals(tc, "give_person", (const char *)msg->parameters[0].v); + CuAssertIntEquals(tc, 2, env.dst->number); + CuAssertIntEquals(tc, 0, env.src->number); + test_cleanup(); +} + +static void test_give_men_requires_contact(CuTest * tc) { + struct give env; + message * msg; + + test_cleanup(); + env.f1 = test_create_faction(0); + env.f2 = test_create_faction(0); + setup_give(&env); + msg = give_men(1, env.src, env.dst, NULL); + CuAssertStrEquals(tc, "feedback_no_contact", (const char *)msg->parameters[3].v); + CuAssertIntEquals(tc, 1, env.dst->number); + CuAssertIntEquals(tc, 1, env.src->number); + test_cleanup(); +} + +static void test_give_men_not_to_self(CuTest * tc) { + struct give env; + message * msg; + test_cleanup(); + env.f2 = env.f1 = test_create_faction(0); + setup_give(&env); + msg = give_men(1, env.src, env.src, NULL); + CuAssertStrEquals(tc, "error10", (const char *)msg->parameters[3].v); + CuAssertIntEquals(tc, 1, env.src->number); + test_cleanup(); +} + +static void test_give_peasants(CuTest * tc) { + struct give env; + message * msg; + + test_cleanup(); + env.f1 = test_create_faction(0); + env.f2 = 0; + setup_give(&env); + rsetpeasants(env.r, 0); + msg = disband_men(1, env.src, NULL); + CuAssertStrEquals(tc, "give_person_peasants", (const char*)msg->parameters[0].v); + CuAssertIntEquals(tc, 0, env.src->number); + CuAssertIntEquals(tc, 1, env.r->land->peasants); + test_cleanup(); +} + static void test_give(CuTest * tc) { struct give env; @@ -110,6 +248,16 @@ CuSuite *get_give_suite(void) { CuSuite *suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_give); + SUITE_ADD_TEST(suite, test_give_men); + SUITE_ADD_TEST(suite, test_give_men_in_ocean); + SUITE_ADD_TEST(suite, test_give_men_none); + SUITE_ADD_TEST(suite, test_give_men_too_many); + SUITE_ADD_TEST(suite, test_give_men_other_faction); + SUITE_ADD_TEST(suite, test_give_men_requires_contact); + SUITE_ADD_TEST(suite, test_give_men_not_to_self); + SUITE_ADD_TEST(suite, test_give_unit_in_ocean); + SUITE_ADD_TEST(suite, test_give_unit_to_peasants); + SUITE_ADD_TEST(suite, test_give_peasants); SUITE_ADD_TEST(suite, test_give_herbs); SUITE_ADD_TEST(suite, test_give_okay); SUITE_ADD_TEST(suite, test_give_denied_by_rules); diff --git a/src/kernel/config.c b/src/kernel/config.c index 83cb2f7cc..3dd22b159 100644 --- a/src/kernel/config.c +++ b/src/kernel/config.c @@ -604,7 +604,7 @@ unsigned int atoip(const char *s) return n; } -bool unit_has_cursed_item(unit * u) +bool unit_has_cursed_item(const unit * u) { item *itm = u->items; while (itm) { diff --git a/src/kernel/config.h b/src/kernel/config.h index ab8916820..0a1f5b242 100644 --- a/src/kernel/config.h +++ b/src/kernel/config.h @@ -234,7 +234,7 @@ extern "C" { struct region *lastregion(struct faction *f); bool idle(struct faction *f); - bool unit_has_cursed_item(struct unit *u); + bool unit_has_cursed_item(const struct unit *u); /* simple garbage collection: */ void *gc_add(void *p); diff --git a/src/kernel/messages.c b/src/kernel/messages.c index d70423251..f7297f832 100644 --- a/src/kernel/messages.c +++ b/src/kernel/messages.c @@ -251,18 +251,22 @@ void addmessage(region * r, faction * f, const char *s, msg_t mtype, int level) caddmessage(r, f, s, mtype, level); } +message * msg_error(const unit * u, struct order *ord, int mno) { + static char msgname[20]; + + if (fval(u->faction, FFL_NPC)) + return 0; + sprintf(msgname, "error%d", mno); + return msg_feedback(u, ord, msgname, ""); +} + message * cmistake(const unit * u, struct order *ord, int mno, int mtype) { - message * result; - static char msgname[20]; - unused_arg(mtype); - - if (fval(u->faction, FFL_NPC)) - return 0; - sprintf(msgname, "error%d", mno); - result = msg_feedback(u, ord, msgname, ""); - ADDMSG(&u->faction->msgs, result); - return result; + message * result; + unused_arg(mtype); + result = msg_error(u, ord, mno); + ADDMSG(&u->faction->msgs, result); + return result; } extern unsigned int new_hashstring(const char *s); diff --git a/src/kernel/messages.h b/src/kernel/messages.h index 5be1a88fb..83c0f2613 100644 --- a/src/kernel/messages.h +++ b/src/kernel/messages.h @@ -55,8 +55,8 @@ extern "C" { #define ADDMSG(msgs, mcreate) { message * m = mcreate; if (m) { assert(m->refcount>=1); add_message(msgs, m); msg_release(m); } } - extern struct message * cmistake(const struct unit *u, struct order *ord, int mno, - int mtype); + struct message * cmistake(const struct unit *u, struct order *ord, int mno, int mtype); + struct message * msg_error(const struct unit * u, struct order *ord, int mno); #ifdef __cplusplus } #endif diff --git a/src/kernel/race.c b/src/kernel/race.c index e83183564..7452fe79d 100644 --- a/src/kernel/race.c +++ b/src/kernel/race.c @@ -172,6 +172,7 @@ race *rc_get_or_create(const char *zName) rc = (race *)calloc(sizeof(race), 1); rc->hitpoints = 1; + rc->recruit_multi = 1.0F; if (strchr(zName, ' ') != NULL) { log_error("race '%s' has an invalid name. remove spaces\n", zName); assert(strchr(zName, ' ') == NULL); diff --git a/src/laws.c b/src/laws.c index b4f00f91f..6420ab366 100755 --- a/src/laws.c +++ b/src/laws.c @@ -4385,6 +4385,7 @@ void init_processor(void) p += 10; /* rest rng again before economics */ add_proc_region(p, &economics, "Zerstoeren, Geben, Rekrutieren, Vergessen"); + add_proc_order(p, K_PROMOTION, &promotion_cmd, 0, "Heldenbefoerderung"); p += 10; if (!keyword_disabled(K_PAY)) { @@ -4456,7 +4457,6 @@ void init_processor(void) p += 10; add_proc_global(p, restack_units, "Einheiten sortieren"); } - add_proc_order(p, K_PROMOTION, &promotion_cmd, 0, "Heldenbefoerderung"); if (!keyword_disabled(K_NUMBER)) { add_proc_order(p, K_NUMBER, &renumber_cmd, 0, "Neue Nummern (Einheiten)"); p += 10; diff --git a/storage b/storage index 6acf2b4fe..bcc2874cf 160000 --- a/storage +++ b/storage @@ -1 +1 @@ -Subproject commit 6acf2b4fec360d7f681275ea3be77002fd2573c7 +Subproject commit bcc2874cf289a1d0fc9cc79ff3ed271403b2e24c