diff --git a/src/creport.c b/src/creport.c index d16f87f22..fd307c5ec 100644 --- a/src/creport.c +++ b/src/creport.c @@ -1558,7 +1558,7 @@ report_computer(const char *filename, report_context * ctx, const char *bom) translate(prefix, LOC(f->locale, prefix))); } fprintf(F, "%d;Rekrutierungskosten\n", f->race->recruitcost); - fprintf(F, "%d;Anzahl Personen\n", count_all(f)); + fprintf(F, "%d;Anzahl Personen\n", f->num_people); fprintf(F, "\"%s\";Magiegebiet\n", magic_school[f->magiegebiet]); if (rc_changed(&rc_cache)) { diff --git a/src/give.c b/src/give.c index 0f1c56c39..1265ab169 100644 --- a/src/give.c +++ b/src/give.c @@ -533,6 +533,23 @@ void give_unit(unit * u, unit * u2, order * ord) } } return; + } else { + int err = checkunitnumber(u2->faction, 1); + if (err) { + if (err == 1) { + ADDMSG(&u->faction->msgs, + msg_feedback(u, ord, + "too_many_units_in_alliance", + "allowed", rule_alliance_limit())); + } + else { + ADDMSG(&u->faction->msgs, + msg_feedback(u, ord, + "too_many_units_in_faction", + "allowed", rule_faction_limit())); + } + return; + } } if (!alliedunit(u2, u->faction, HELP_GIVE) && ucontact(u2, u) == 0) { diff --git a/src/give.test.c b/src/give.test.c index 1767dec36..33a9ebfd9 100644 --- a/src/give.test.c +++ b/src/give.test.c @@ -54,26 +54,12 @@ static void setup_give(struct give *env) { } } -static void test_give_unit_to_peasants(CuTest * tc) { - struct give env = { 0 }; - test_setup_ex(tc); - env.f1 = test_create_faction(0); - env.f2 = 0; - setup_give(&env); - rsetpeasants(env.r, 0); - give_unit(env.src, NULL, NULL); - CuAssertIntEquals(tc, 0, env.src->number); - CuAssertIntEquals(tc, 1, rpeasants(env.r)); - test_cleanup(); -} - static void test_give_unit(CuTest * tc) { struct give env = { 0 }; test_setup_ex(tc); env.f1 = test_create_faction(0); env.f2 = test_create_faction(0); setup_give(&env); - env.r->terrain = test_create_terrain("ocean", SEA_REGION); config_set("rules.give.max_men", "0"); give_unit(env.src, env.dst, NULL); CuAssertPtrEquals(tc, env.f1, env.src->faction); @@ -88,7 +74,38 @@ static void test_give_unit(CuTest * tc) { test_cleanup(); } -static void test_give_unit_in_ocean(CuTest * tc) { +static void test_give_unit_limits(CuTest * tc) { + struct give env = { 0 }; + test_setup_ex(tc); + env.f1 = test_create_faction(0); + env.f2 = test_create_faction(0); + setup_give(&env); + CuAssertIntEquals(tc, 1, env.f1->num_units); + CuAssertIntEquals(tc, 1, env.f2->num_units); + config_set("rules.limit.faction", "1"); + give_unit(env.src, env.dst, NULL); + CuAssertPtrEquals(tc, env.f1, env.src->faction); + CuAssertIntEquals(tc, 0, env.f2->newbies); + CuAssertIntEquals(tc, 1, env.f1->num_units); + CuAssertIntEquals(tc, 1, env.f2->num_units); + CuAssertPtrNotNull(tc, test_find_messagetype(env.f1->msgs, "too_many_units_in_faction")); + test_cleanup(); +} + +static void test_give_unit_to_peasants(CuTest * tc) { + struct give env = { 0 }; + test_setup_ex(tc); + env.f1 = test_create_faction(0); + env.f2 = 0; + setup_give(&env); + rsetpeasants(env.r, 0); + give_unit(env.src, NULL, NULL); + CuAssertIntEquals(tc, 0, env.src->number); + CuAssertIntEquals(tc, 1, rpeasants(env.r)); + test_cleanup(); +} + +static void test_give_unit_to_ocean(CuTest * tc) { struct give env = { 0 }; test_setup_ex(tc); env.f1 = test_create_faction(0); @@ -429,7 +446,8 @@ CuSuite *get_give_suite(void) 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); - SUITE_ADD_TEST(suite, test_give_unit_in_ocean); + SUITE_ADD_TEST(suite, test_give_unit_limits); + SUITE_ADD_TEST(suite, test_give_unit_to_ocean); SUITE_ADD_TEST(suite, test_give_unit_to_peasants); SUITE_ADD_TEST(suite, test_give_peasants); SUITE_ADD_TEST(suite, test_give_herbs); diff --git a/src/kernel/alliance.c b/src/kernel/alliance.c index 0feb8407d..910670d10 100644 --- a/src/kernel/alliance.c +++ b/src/kernel/alliance.c @@ -239,8 +239,25 @@ static void perform_transfer(void) } } +bool num_units_cb(void *entry, void *more) { + faction *f = (faction *)entry; + int *num = (int *)more; + *num += f->num_units; + return true; +} + +int alliance_size(const alliance *al) +{ + int num = 0; + if (al) { + selist_foreach_ex(al->members, num_units_cb, &num); + } + return num; +} + static void perform_join(void) { + int alimit = rule_alliance_limit(); alliance_transaction **tap = transactions + ALLIANCE_JOIN; while (*tap) { alliance_transaction *ta = *tap; @@ -270,7 +287,16 @@ static void perform_join(void) ti = *tip; } if (ti) { - setalliance(fj, al); + int maxsize = (alimit > 0) ? (alimit - alliance_size(al)) : 0; + if (alimit > 0 && fj->num_units > maxsize) { + ADDMSG(&fj->msgs, + msg_feedback(ta->u, ta->ord, + "too_many_units_in_alliance", + "allowed", alimit)); + } + else { + setalliance(fj, al); + } *tip = ti->next; free(ti); } diff --git a/src/kernel/alliance.h b/src/kernel/alliance.h index 8e112e8c9..00b4b29e4 100644 --- a/src/kernel/alliance.h +++ b/src/kernel/alliance.h @@ -67,7 +67,7 @@ extern "C" { struct faction *alliance_get_leader(struct alliance *al); void alliance_cmd(void); bool is_allied(const struct faction *f1, const struct faction *f2); - + int alliance_size(const struct alliance *al); /* #units in the alliance */ void alliance_setname(alliance * self, const char *name); /* execute commands */ diff --git a/src/kernel/alliance.test.c b/src/kernel/alliance.test.c index f63767672..885bc8413 100644 --- a/src/kernel/alliance.test.c +++ b/src/kernel/alliance.test.c @@ -112,6 +112,29 @@ static void test_alliance_cmd(CuTest *tc) { test_cleanup(); } +static void test_alliance_limits(CuTest *tc) { + unit *u1, *u2; + struct region *r; + + test_setup(); + r = test_create_region(0, 0, 0); + u1 = test_create_unit(test_create_faction(0), r); + u2 = test_create_unit(test_create_faction(0), r); + + config_set("rules.limit.alliance", "1"); + unit_addorder(u1, create_order(K_ALLIANCE, u1->faction->locale, "%s %s", alliance_kwd[ALLIANCE_NEW], itoa36(42))); + unit_addorder(u1, create_order(K_ALLIANCE, u1->faction->locale, "%s %s", alliance_kwd[ALLIANCE_INVITE], itoa36(u2->faction->no))); + unit_addorder(u2, create_order(K_ALLIANCE, u1->faction->locale, "%s %s", alliance_kwd[ALLIANCE_JOIN], itoa36(42))); + CuAssertTrue(tc, !is_allied(u1->faction, u2->faction)); + CuAssertPtrEquals(tc, 0, f_get_alliance(u1->faction)); + alliance_cmd(); + CuAssertPtrNotNull(tc, f_get_alliance(u1->faction)); + CuAssertPtrEquals(tc, 0, f_get_alliance(u2->faction)); + CuAssertTrue(tc, !is_allied(u1->faction, u2->faction)); + CuAssertPtrNotNull(tc, test_find_messagetype(u2->faction->msgs, "too_many_units_in_alliance")); + test_cleanup(); +} + static void test_alliance_cmd_kick(CuTest *tc) { unit *u1, *u2; struct region *r; @@ -200,6 +223,7 @@ CuSuite *get_alliance_suite(void) SUITE_ADD_TEST(suite, test_alliance_dead_faction); SUITE_ADD_TEST(suite, test_alliance_make); SUITE_ADD_TEST(suite, test_alliance_join); + SUITE_ADD_TEST(suite, test_alliance_limits); SUITE_ADD_TEST(suite, test_alliance_cmd); SUITE_ADD_TEST(suite, test_alliance_cmd_no_invite); SUITE_ADD_TEST(suite, test_alliance_cmd_kick); diff --git a/src/kernel/faction.c b/src/kernel/faction.c index 139474048..03cea282f 100755 --- a/src/kernel/faction.c +++ b/src/kernel/faction.c @@ -712,7 +712,7 @@ int count_faction(const faction * f, int flags) int n = 0; for (u = f->units; u; u = u->nextF) { const race *rc = u_race(u); - int x = (flags&COUNT_UNITS) ? 1 : u->number; + int x = u->number; if (f->race != rc) { if (!playerrace(rc)) { if (flags&COUNT_MONSTERS) { @@ -732,16 +732,6 @@ int count_faction(const faction * f, int flags) return n; } -int count_units(const faction * f) -{ - return count_faction(f, COUNT_ALL | COUNT_UNITS); -} - -int count_all(const faction * f) -{ - return count_faction(f, COUNT_ALL); -} - int count_migrants(const faction * f) { return count_faction(f, COUNT_MIGRANTS); @@ -752,7 +742,7 @@ int count_maxmigrants(const faction * f) int formula = rc_migrants_formula(f->race); if (formula == MIGRANTS_LOG10) { - int nsize = count_all(f); + int nsize = f->num_people; if (nsize > 0) { int x = (int)(log10(nsize / 50.0) * 20); if (x < 0) x = 0; diff --git a/src/kernel/faction.h b/src/kernel/faction.h index 0a0ac39d6..9b290e5da 100644 --- a/src/kernel/faction.h +++ b/src/kernel/faction.h @@ -77,13 +77,11 @@ extern "C" { magic_t magiegebiet; int newbies; int num_people; /* Anzahl Personen ohne Monster */ - int num_total; /* Anzahl Personen mit Monstern */ + int num_units; int options; - int no_units; struct ally *allies; /* alliedgroup and others should check sf.faction.alive before using a faction from f.allies */ struct group *groups; /* alliedgroup and others should check sf.faction.alive before using a faction from f.groups */ int nregions; - int money; score_t score; struct alliance *alliance; int alliance_joindate; /* the turn on which the faction joined its current alliance (or left the last one) */ @@ -166,14 +164,10 @@ extern "C" { #define COUNT_MONSTERS 0x01 #define COUNT_MIGRANTS 0x02 #define COUNT_DEFAULT 0x04 -#define COUNT_ALL 0x07 -#define COUNT_UNITS 0x10 int count_faction(const struct faction * f, int flags); int count_migrants(const struct faction * f); int count_maxmigrants(const struct faction * f); - int count_all(const struct faction * f); - int count_units(const struct faction * f); int max_magicians(const struct faction * f); struct faction *getfaction(void); diff --git a/src/kernel/save.c b/src/kernel/save.c index e487737f9..5be078d8a 100644 --- a/src/kernel/save.c +++ b/src/kernel/save.c @@ -657,10 +657,7 @@ unit *read_unit(struct gamedata *data) if (f != u->faction) { u_setfaction(u, f); } - if (u->faction) { - ++u->faction->no_units; - } - else { + if (!u->faction) { log_error("unit %s has faction == NULL", itoa36(u->no)); return 0; } diff --git a/src/kernel/unit.c b/src/kernel/unit.c index 595accba2..2239cf81b 100644 --- a/src/kernel/unit.c +++ b/src/kernel/unit.c @@ -1129,7 +1129,7 @@ void u_setfaction(unit * u, faction * f) if (u->faction == f) return; if (u->faction) { - --u->faction->no_units; + --u->faction->num_units; set_number(u, 0); join_group(u, NULL); free_orders(&u->orders); @@ -1166,7 +1166,7 @@ void u_setfaction(unit * u, faction * f) set_number(u, cnt); } if (f) { - ++f->no_units; + ++f->num_units; } } @@ -1581,7 +1581,7 @@ unit *create_unit(region * r, faction * f, int number, const struct race *urace, int maxheroes(const struct faction *f) { - int nsize = count_all(f); + int nsize = f->num_people; if (nsize == 0) return 0; else { diff --git a/src/laws.c b/src/laws.c index 5b4c5971b..736510cd2 100644 --- a/src/laws.c +++ b/src/laws.c @@ -1279,8 +1279,8 @@ static void remove_idle_players(void) } else if (turn != f->lastorders) { char info[256]; - sprintf(info, "%d Einheiten, %d Personen, %d Silber", - f->no_units, f->num_total, f->money); + sprintf(info, "%d Einheiten, %d Personen", + f->num_units, f->num_people); } fp = &f->next; } @@ -2519,7 +2519,7 @@ int promotion_cmd(unit * u, struct order *ord) u_race(u))); return 0; } - people = count_all(u->faction) * u->number; + people = u->faction->num_people * u->number; money = get_pooled(u, rsilver, GET_ALL, people); if (people > money) { @@ -3020,8 +3020,7 @@ static int maxunits(const faction * f) int checkunitnumber(const faction * f, int add) { int alimit, flimit; - int flags = COUNT_DEFAULT | COUNT_MIGRANTS | COUNT_UNITS; - int fno = count_faction(f, flags) + add; + int fno = f->num_units + add; flimit = rule_faction_limit(); if (flimit && fno > flimit) { return 2; @@ -3029,22 +3028,10 @@ int checkunitnumber(const faction * f, int add) alimit = rule_alliance_limit(); if (alimit) { - /* if unitsperalliance is true, maxunits returns the - number of units allowed in an alliance */ - faction *f2; - int unitsinalliance = fno; - if (unitsinalliance > alimit) { + int unitsinalliance = alliance_size(f->alliance); + if (unitsinalliance + add > alimit) { return 1; } - - for (f2 = factions; f2; f2 = f2->next) { - if (f != f2 && f->alliance == f2->alliance) { - unitsinalliance += count_faction(f2, flags); - if (unitsinalliance > alimit) { - return 1; - } - } - } } return 0; @@ -4285,12 +4272,18 @@ void update_subscriptions(void) fclose(F); } -bool -cansee(const faction * f, const region * r, const unit * u, int modifier) -/* r kann != u->region sein, wenn es um Durchreisen geht, +/** 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 in (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) { int stealth, rings; unit *u2 = r->units; diff --git a/src/laws.h b/src/laws.h index 090d2b978..7f1385483 100755 --- a/src/laws.h +++ b/src/laws.h @@ -58,6 +58,7 @@ extern "C" { void turn_process(void); void turn_end(void); + int checkunitnumber(const struct faction * f, int add); void new_units(void); void defaultorders(void); void quit(void); diff --git a/src/laws.test.c b/src/laws.test.c index 937754988..e8391b67c 100644 --- a/src/laws.test.c +++ b/src/laws.test.c @@ -5,6 +5,7 @@ #include "monsters.h" #include +#include #include #include #include @@ -427,7 +428,51 @@ static void test_unit_limit(CuTest * tc) test_cleanup(); } -extern int checkunitnumber(const faction * f, int add); +static void test_limit_new_units(CuTest * tc) +{ + faction *f; + unit *u; + alliance *al; + + test_setup(); + al = makealliance(1, "Hodor"); + f = test_create_faction(NULL); + u = test_create_unit(f, test_create_region(0, 0, NULL)); + CuAssertIntEquals(tc, 1, f->num_units); + CuAssertIntEquals(tc, 1, f->num_people); + scale_number(u, 10); + CuAssertIntEquals(tc, 10, f->num_people); + config_set("rules.limit.faction", "2"); + + u->orders = create_order(K_MAKETEMP, f->locale, "1"); + new_units(); + CuAssertPtrNotNull(tc, u->next); + CuAssertIntEquals(tc, 2, f->num_units); + + new_units(); + CuAssertIntEquals(tc, 2, f->num_units); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "too_many_units_in_faction")); + + setalliance(f, al); + + config_set("rules.limit.faction", "3"); + config_set("rules.limit.alliance", "2"); + + new_units(); + CuAssertIntEquals(tc, 2, f->num_units); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "too_many_units_in_alliance")); + + config_set("rules.limit.alliance", "3"); + u = test_create_unit(test_create_faction(NULL), u->region); + setalliance(u->faction, al); + + new_units(); + CuAssertIntEquals(tc, 2, f->num_units); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "too_many_units_in_alliance")); + + test_cleanup(); +} + static void test_cannot_create_unit_above_limit(CuTest * tc) { faction *f; @@ -1535,6 +1580,7 @@ CuSuite *get_laws_suite(void) SUITE_ADD_TEST(suite, test_fishing_does_not_give_goblins_money); SUITE_ADD_TEST(suite, test_fishing_gets_reset); SUITE_ADD_TEST(suite, test_unit_limit); + SUITE_ADD_TEST(suite, test_limit_new_units); SUITE_ADD_TEST(suite, test_update_guards); SUITE_ADD_TEST(suite, test_newbie_cannot_guard); SUITE_ADD_TEST(suite, test_unarmed_cannot_guard); diff --git a/src/modules/score.c b/src/modules/score.c index 0776d6507..efb3465d4 100644 --- a/src/modules/score.c +++ b/src/modules/score.c @@ -160,7 +160,7 @@ void score(void) faction *f; fwrite(utf8_bom, 1, 3, scoreFP); for (f = factions; f; f = f->next) - if (!fval(f, FFL_NPC) && f->num_total != 0) { + if (!fval(f, FFL_NPC) && f->num_people != 0) { char score[32]; write_score(score, sizeof(score), f->score); fprintf(scoreFP, "%s ", score); @@ -195,7 +195,7 @@ void score(void) if (f->alliance && f->alliance == a) { alliance_factions++; alliance_score += f->score; - alliance_number += f->num_total; + alliance_number += f->num_people; if (token != NULL) { unit *u = f->units; while (u != NULL) { diff --git a/src/report.c b/src/report.c index 2f8ba7bce..dbcf54ff1 100644 --- a/src/report.c +++ b/src/report.c @@ -2106,14 +2106,8 @@ report_plaintext(const char *filename, report_context * ctx, RENDER(f, buf, sizeof(buf), ("nr_score", "score average", score, avg)); centre(out, buf, true); } - no_units = count_units(f); - no_people = count_all(f); - if (f->flags & FFL_NPC) { - no_people = f->num_total; - } - else { - no_people = f->num_people; - } + no_units = f->num_units; + no_people = f->num_people; m = msg_message("nr_population", "population units limit", no_people, no_units, rule_faction_limit()); nr_render(m, f->locale, buf, sizeof(buf), f); msg_release(m); diff --git a/src/summary.c b/src/summary.c index 32416a263..71bb30efc 100644 --- a/src/summary.c +++ b/src/summary.c @@ -129,15 +129,15 @@ static char *rcomp(int i, int j) static void out_faction(FILE * file, const struct faction *f) { if (alliances != NULL) { - fprintf(file, "%s (%s/%d) (%.3s/%.3s), %d Einh., %d Pers., $%d, %d NMR\n", + fprintf(file, "%s (%s/%d) (%.3s/%.3s), %d Einh., %d Pers., %d NMR\n", f->name, itoa36(f->no), f_get_alliance(f) ? f->alliance->id : 0, LOC(default_locale, rc_name_s(f->race, NAME_SINGULAR)), magic_school[f->magiegebiet], - count_units(f), f->num_total, f->money, turn - f->lastorders); + f->num_units, f->num_people, turn - f->lastorders); } else { - fprintf(file, "%s (%.3s/%.3s), %d Einh., %d Pers., $%d, %d NMR\n", + fprintf(file, "%s (%.3s/%.3s), %d Einh., %d Pers., %d NMR\n", factionname(f), LOC(default_locale, rc_name_s(f->race, NAME_SINGULAR)), - magic_school[f->magiegebiet], count_units(f), f->num_total, f->money, + magic_school[f->magiegebiet], f->num_units, f->num_people, turn - f->lastorders); } } @@ -394,8 +394,7 @@ summary *make_summary(void) } ++plang->number; f->nregions = 0; - f->num_total = 0; - f->money = 0; + f->num_people = 0; if (f->units) { s->factions++; /* Problem mit Monsterpartei ... */ @@ -477,8 +476,7 @@ summary *make_summary(void) } } - f->num_total += u->number; - f->money += get_money(u); + f->num_people += u->number; orace = (int)old_race(u_race(u)); if (orace >= 0) { s->poprace[orace] += u->number;