Merge branch '2541-quit' into develop

This commit is contained in:
Enno Rehling 2019-05-04 12:01:55 +02:00
commit 5c7788ecdd
17 changed files with 312 additions and 73 deletions

View File

@ -2937,13 +2937,6 @@
<arg name="command" type="order"/> <arg name="command" type="order"/>
</type> </type>
</message> </message>
<message name="error282" section="errors">
<type>
<arg name="unit" type="unit"/>
<arg name="region" type="region"/>
<arg name="command" type="order"/>
</type>
</message>
<message name="error281" section="errors"> <message name="error281" section="errors">
<type> <type>
<arg name="unit" type="unit"/> <arg name="unit" type="unit"/>
@ -3244,13 +3237,6 @@
<arg name="target" type="region"/> <arg name="target" type="region"/>
</type> </type>
</message> </message>
<message name="error241" section="errors">
<type>
<arg name="unit" type="unit"/>
<arg name="region" type="region"/>
<arg name="command" type="order"/>
</type>
</message>
<message name="error240" section="errors"> <message name="error240" section="errors">
<type> <type>
<arg name="unit" type="unit"/> <arg name="unit" type="unit"/>

View File

@ -587,9 +587,6 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - Unbekannte Opti
msgid "error131" msgid "error131"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Um in Gletschern Straßen bauen zu können, muß zuerst ein Tunnel errichtet werden.\"" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Um in Gletschern Straßen bauen zu können, muß zuerst ein Tunnel errichtet werden.\""
msgid "error241"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Die Partei muß mindestens 81 Wochen alt sein, um einen Neustart mit einer anderen Rasse zu versuchen.\""
msgid "feedback_unit_not_found" msgid "feedback_unit_not_found"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Die Einheit wurde nicht gefunden.\"" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Die Einheit wurde nicht gefunden.\""
@ -1119,7 +1116,7 @@ msgid "dissolve_units_4"
msgstr "\"$unit($unit) in $region($region): $int($number) $race($race,$number) $if($eq($number,1),\"zerfiel\", \"zerfielen\") zu Staub.\"" msgstr "\"$unit($unit) in $region($region): $int($number) $race($race,$number) $if($eq($number,1),\"zerfiel\", \"zerfielen\") zu Staub.\""
msgid "error281" msgid "error281"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Gegen welche Rasse soll der Jihad ausgerufen werden?\"" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Die Zielpartei muss die selbe Rasse haben.\""
msgid "error171" msgid "error171"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Diesen Kampfzauber gibt es nicht.\"" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Diesen Kampfzauber gibt es nicht.\""
@ -1784,9 +1781,6 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - Es konnte kein
msgid "error50" msgid "error50"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Die Einheit ist nicht erfahren genug dafür.\"" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Die Einheit ist nicht erfahren genug dafür.\""
msgid "error282"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Gegen diese Rasse kann kein Jihad ausgerufen werden.\""
msgid "nmr_warning_final" msgid "nmr_warning_final"
msgstr "\"Bitte sende die Befehle nächste Runde ein, wenn du weiterspielen möchtest.\"" msgstr "\"Bitte sende die Befehle nächste Runde ein, wenn du weiterspielen möchtest.\""

View File

@ -587,9 +587,6 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - Unknown option.
msgid "error131" msgid "error131"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - You must build a tunnel before building roads through glaciers.\"" msgstr "\"$unit($unit) in $region($region): '$order($command)' - You must build a tunnel before building roads through glaciers.\""
msgid "error241"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - The faction must be at least 81 weeks old to restart with a new race.\""
msgid "feedback_unit_not_found" msgid "feedback_unit_not_found"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - The unit could not be found.\"" msgstr "\"$unit($unit) in $region($region): '$order($command)' - The unit could not be found.\""
@ -1119,7 +1116,7 @@ msgid "dissolve_units_4"
msgstr "\"$unit($unit) in $region($region): $int($number) $race($race,$number) turned to dust.\"" msgstr "\"$unit($unit) in $region($region): $int($number) $race($race,$number) turned to dust.\""
msgid "error281" msgid "error281"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - What race did you want the jihad to be against?\"" msgstr "\"$unit($unit) in $region($region): '$order($command)' - The target faction must have the same race as yours.\""
msgid "error171" msgid "error171"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - This combat spell does not exist.\"" msgstr "\"$unit($unit) in $region($region): '$order($command)' - This combat spell does not exist.\""
@ -1784,9 +1781,6 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - No peasant coul
msgid "error50" msgid "error50"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - The unit is not experienced enough to do this.\"" msgstr "\"$unit($unit) in $region($region): '$order($command)' - The unit is not experienced enough to do this.\""
msgid "error282"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - You cannot start a jihad against this race.\""
msgid "nmr_warning_final" msgid "nmr_warning_final"
msgstr "\"Please send in orders for the next turn if you want to continue playing.\"" msgstr "\"Please send in orders for the next turn if you want to continue playing.\""

View File

@ -548,7 +548,7 @@ static void recruit(unit * u, struct order *ord, econ_request ** recruitorders)
return; return;
} }
if (has_skill(u, SK_ALCHEMY)) { if (has_skill(u, SK_ALCHEMY)) {
if (count_skill(u->faction, SK_ALCHEMY) + n > skill_limit(u->faction, SK_ALCHEMY)) { if (faction_count_skill(u->faction, SK_ALCHEMY) + n > faction_skill_limit(u->faction, SK_ALCHEMY)) {
cmistake(u, ord, 156, MSG_EVENT); cmistake(u, ord, 156, MSG_EVENT);
return; return;
} }

View File

@ -299,7 +299,7 @@ static bool can_give_men(const unit *u, const unit *dst, order *ord, message **m
return false; return false;
} }
static bool rule_transfermen(void) bool rule_transfermen(void)
{ {
int rule = config_get_int("rules.transfermen", 1); int rule = config_get_int("rules.transfermen", 1);
return rule != 0; return rule != 0;
@ -391,7 +391,7 @@ message * give_men(int n, unit * u, unit * u2, struct order *ord)
} }
if (has_skill(u, SK_ALCHEMY) || has_skill(u2, SK_ALCHEMY)) { if (has_skill(u, SK_ALCHEMY) || has_skill(u2, SK_ALCHEMY)) {
int k = count_skill(u2->faction, SK_ALCHEMY); int k = faction_count_skill(u2->faction, SK_ALCHEMY);
/* Falls die Zieleinheit keine Alchemisten sind, werden sie nun /* Falls die Zieleinheit keine Alchemisten sind, werden sie nun
* welche. */ * welche. */
@ -408,7 +408,7 @@ message * give_men(int n, unit * u, unit * u2, struct order *ord)
/* wird das Alchemistenmaximum ueberschritten ? */ /* wird das Alchemistenmaximum ueberschritten ? */
if (k > skill_limit(u2->faction, SK_ALCHEMY)) { if (k > faction_skill_limit(u2->faction, SK_ALCHEMY)) {
error = 156; error = 156;
} }
} }
@ -472,9 +472,23 @@ message * disband_men(int n, unit * u, struct order *ord) {
return msg_message("give_person_peasants", "unit amount", u, n); return msg_message("give_person_peasants", "unit amount", u, n);
} }
int give_unit_allowed(const unit * u)
{
if (unit_has_cursed_item(u)) {
return 78;
}
if (fval(u, UFL_HERO)) {
return 75;
}
if (fval(u, UFL_LOCKED) || fval(u, UFL_HUNGER)) {
return 74;
}
return 0;
}
void give_unit(unit * u, unit * u2, order * ord) void give_unit(unit * u, unit * u2, order * ord)
{ {
int maxt = max_transfers(); int err, maxt = max_transfers();
assert(u); assert(u);
if (!rule_transfermen() && u2 && u->faction != u2->faction) { if (!rule_transfermen() && u2 && u->faction != u2->faction) {
@ -482,17 +496,9 @@ void give_unit(unit * u, unit * u2, order * ord)
return; return;
} }
if (unit_has_cursed_item(u)) { err = give_unit_allowed(u);
cmistake(u, ord, 78, MSG_COMMERCE); if (err != 0) {
return; cmistake(u, ord, err, MSG_COMMERCE);
}
if (fval(u, UFL_HERO)) {
cmistake(u, ord, 75, MSG_COMMERCE);
return;
}
if (fval(u, UFL_LOCKED) || fval(u, UFL_HUNGER)) {
cmistake(u, ord, 74, MSG_COMMERCE);
return; return;
} }
@ -586,8 +592,8 @@ void give_unit(unit * u, unit * u2, order * ord)
} }
} }
if (has_skill(u, SK_MAGIC)) { if (has_skill(u, SK_MAGIC)) {
if (count_skill(u2->faction, SK_MAGIC) + u->number > if (faction_count_skill(u2->faction, SK_MAGIC) + u->number >
skill_limit(u2->faction, SK_MAGIC)) { faction_skill_limit(u2->faction, SK_MAGIC)) {
cmistake(u, ord, 155, MSG_COMMERCE); cmistake(u, ord, 155, MSG_COMMERCE);
return; return;
} }
@ -597,8 +603,8 @@ void give_unit(unit * u, unit * u2, order * ord)
} }
} }
if (has_skill(u, SK_ALCHEMY) if (has_skill(u, SK_ALCHEMY)
&& count_skill(u2->faction, SK_ALCHEMY) + u->number > && faction_count_skill(u2->faction, SK_ALCHEMY) + u->number >
skill_limit(u2->faction, SK_ALCHEMY)) { faction_skill_limit(u2->faction, SK_ALCHEMY)) {
cmistake(u, ord, 156, MSG_COMMERCE); cmistake(u, ord, 156, MSG_COMMERCE);
return; return;
} }

View File

@ -29,10 +29,12 @@ extern "C" {
struct message * disband_men(int n, struct unit * u, struct order *ord); 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 message * give_men(int n, struct unit *u, struct unit *u2,
struct order *ord); struct order *ord);
int give_unit_allowed(const struct unit * u);
void give_unit(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); void give_cmd(struct unit * u, struct order * ord);
struct message * check_give(const struct unit * u, const struct unit * u2, struct order *ord); struct message * check_give(const struct unit * u, const struct unit * u2, struct order *ord);
bool can_give_to(struct unit *u, struct unit *u2); bool can_give_to(struct unit *u, struct unit *u2);
bool rule_transfermen(void);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -572,7 +572,7 @@ static int build_limited(unit * u, const construction * con, int completed, int
} }
/* Flinkfingerring wirkt nicht auf Mengenbegrenzte (magische) /* Flinkfingerring wirkt nicht auf Mengenbegrenzte (magische)
* Talente */ * Talente */
if (skill_limit(u->faction, con->skill) == INT_MAX) { if (faction_skill_limit(u->faction, con->skill) == INT_MAX) {
const resource_type *ring = get_resourcetype(R_RING_OF_NIMBLEFINGER); const resource_type *ring = get_resourcetype(R_RING_OF_NIMBLEFINGER);
item *itm = ring ? *i_find(&u->items, ring->itype) : 0; item *itm = ring ? *i_find(&u->items, ring->itype) : 0;
int i = itm ? itm->number : 0; int i = itm ? itm->number : 0;

View File

@ -609,7 +609,7 @@ static int allied_skillcount(const faction * f, skill_t sk)
for (qi = 0; members; selist_advance(&members, &qi, 1)) { for (qi = 0; members; selist_advance(&members, &qi, 1)) {
faction *m = (faction *)selist_get(members, qi); faction *m = (faction *)selist_get(members, qi);
num += count_skill(m, sk); num += faction_count_skill(m, sk);
} }
return num; return num;
} }
@ -623,7 +623,7 @@ static int allied_skilllimit(const faction * f, skill_t sk)
return value; return value;
} }
int count_skill(faction * f, skill_t sk) int faction_count_skill(faction * f, skill_t sk)
{ {
int n = 0; int n = 0;
unit *u; unit *u;
@ -638,7 +638,7 @@ int count_skill(faction * f, skill_t sk)
return n; return n;
} }
int skill_limit(faction * f, skill_t sk) int faction_skill_limit(const faction * f, skill_t sk)
{ {
int m = INT_MAX; int m = INT_MAX;
int al = allied_skilllimit(f, sk); int al = allied_skilllimit(f, sk);

View File

@ -161,8 +161,8 @@ extern "C" {
struct spellbook * faction_get_spellbook(struct faction *f); struct spellbook * faction_get_spellbook(struct faction *f);
/* skills */ /* skills */
int skill_limit(struct faction *f, skill_t sk); int faction_skill_limit(const struct faction *f, skill_t sk);
int count_skill(struct faction *f, skill_t sk); int faction_count_skill(struct faction *f, skill_t sk);
bool faction_id_is_unused(int); bool faction_id_is_unused(int);
#define COUNT_MONSTERS 0x01 #define COUNT_MONSTERS 0x01

View File

@ -25,6 +25,7 @@
#include <assert.h> #include <assert.h>
#include <stdio.h> #include <stdio.h>
#include <limits.h>
static void test_destroyfaction_allies(CuTest *tc) { static void test_destroyfaction_allies(CuTest *tc) {
faction *f1, *f2; faction *f1, *f2;
@ -211,6 +212,26 @@ static void test_max_migrants(CuTest *tc) {
test_teardown(); test_teardown();
} }
static void test_skill_limit(CuTest *tc) {
faction *f;
test_setup();
f = test_create_faction(NULL);
CuAssertIntEquals(tc, INT_MAX, faction_skill_limit(f, SK_ENTERTAINMENT));
CuAssertIntEquals(tc, 3, faction_skill_limit(f, SK_ALCHEMY));
config_set_int("rules.maxskills.alchemy", 4);
CuAssertIntEquals(tc, 4, faction_skill_limit(f, SK_ALCHEMY));
CuAssertIntEquals(tc, 3, faction_skill_limit(f, SK_MAGIC));
CuAssertIntEquals(tc, 3, max_magicians(f));
config_set_int("rules.maxskills.magic", 4);
CuAssertIntEquals(tc, 4, faction_skill_limit(f, SK_MAGIC));
CuAssertIntEquals(tc, 4, max_magicians(f));
f->race = test_create_race(racenames[RC_ELF]);
CuAssertIntEquals(tc, 5, faction_skill_limit(f, SK_MAGIC));
CuAssertIntEquals(tc, 5, max_magicians(f));
test_teardown();
}
static void test_valid_race(CuTest *tc) { static void test_valid_race(CuTest *tc) {
race * rc1, *rc2; race * rc1, *rc2;
faction *f; faction *f;
@ -334,6 +355,7 @@ CuSuite *get_faction_suite(void)
CuSuite *suite = CuSuiteNew(); CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_addplayer); SUITE_ADD_TEST(suite, test_addplayer);
SUITE_ADD_TEST(suite, test_max_migrants); SUITE_ADD_TEST(suite, test_max_migrants);
SUITE_ADD_TEST(suite, test_skill_limit);
SUITE_ADD_TEST(suite, test_addfaction); SUITE_ADD_TEST(suite, test_addfaction);
SUITE_ADD_TEST(suite, test_remove_empty_factions); SUITE_ADD_TEST(suite, test_remove_empty_factions);
SUITE_ADD_TEST(suite, test_destroyfaction_allies); SUITE_ADD_TEST(suite, test_destroyfaction_allies);

View File

@ -1812,19 +1812,29 @@ int maintenance_cost(const struct unit *u)
return u_race(u)->maintenance * u->number; return u_race(u)->maintenance * u->number;
} }
static skill_t limited_skills[] = { SK_MAGIC, SK_ALCHEMY, SK_TACTICS, SK_SPY, SK_HERBALISM, NOSKILL }; static skill_t limited_skills[] = { SK_ALCHEMY, SK_HERBALISM, SK_MAGIC, SK_SPY, SK_TACTICS, NOSKILL };
bool is_limited_skill(skill_t sk)
{
int i;
for (i = 0; limited_skills[i] != NOSKILL; ++i) {
if (sk == limited_skills[i]) {
return true;
}
}
return false;
}
bool has_limited_skills(const struct unit * u) bool has_limited_skills(const struct unit * u)
{ {
int i, j; int i;
for (i = 0; i != u->skill_size; ++i) { for (i = 0; i != u->skill_size; ++i) {
skill *sv = u->skills + i; skill *sv = u->skills + i;
for (j = 0; limited_skills[j] != NOSKILL; ++j) { if (is_limited_skill(sv->id)) {
if (sv->id == limited_skills[j]) {
return true; return true;
} }
} }
}
return false; return false;
} }

View File

@ -251,6 +251,7 @@ extern "C" {
bool has_horses(const struct unit *u); bool has_horses(const struct unit *u);
int maintenance_cost(const struct unit *u); int maintenance_cost(const struct unit *u);
bool has_limited_skills(const struct unit *u); bool has_limited_skills(const struct unit *u);
bool is_limited_skill(skill_t sk);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -30,6 +30,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "battle.h" #include "battle.h"
#include "contact.h" #include "contact.h"
#include "economy.h" #include "economy.h"
#include "give.h"
#include "market.h" #include "market.h"
#include "morale.h" #include "morale.h"
#include "monsters.h" #include "monsters.h"
@ -395,8 +396,7 @@ static void peasants(region * r, int rule)
dead += (int)(0.5 + n * PEASANT_STARVATION_CHANCE); dead += (int)(0.5 + n * PEASANT_STARVATION_CHANCE);
if (dead > 0) { if (dead > 0) {
message *msg = add_message(&r->msgs, msg_message("phunger", "dead", dead)); ADDMSG(&r->msgs, msg_message("phunger", "dead", dead));
msg_release(msg);
peasants -= dead; peasants -= dead;
} }
@ -941,6 +941,52 @@ int leave_cmd(unit * u, struct order *ord)
return 0; return 0;
} }
void transfer_faction(faction *fsrc, faction *fdst) {
unit *u;
skill_t sk;
int skill_count[MAXSKILLS];
int skill_limit[MAXSKILLS];
for (sk = 0; sk != MAXSKILLS; ++sk) {
skill_limit[sk] = faction_skill_limit(fdst, sk);
}
memset(skill_count, 0, sizeof(skill_count));
for (u = fdst->units; u != NULL; u = u->nextF) {
if (u->skills) {
int i;
for (i = 0; i != u->skill_size; ++i) {
const skill *sv = u->skills + i;
skill_t sk = (skill_t)sv->id;
skill_count[sk] += u->number;
}
}
}
for (u = fsrc->units; u != NULL; u = u->nextF) {
if (u_race(u) == fdst->race) {
u->flags &= ~UFL_HERO;
if (give_unit_allowed(u) == 0) {
if (u->skills) {
int i;
for (i = 0; i != u->skill_size; ++i) {
const skill *sv = u->skills + i;
skill_t sk = (skill_t)sv->id;
if (skill_count[sk] + u->number > skill_limit[sk]) {
break;
}
}
if (i != u->skill_size) {
continue;
}
}
u_setfaction(u, fdst);
}
}
}
}
int quit_cmd(unit * u, struct order *ord) int quit_cmd(unit * u, struct order *ord)
{ {
char token[128]; char token[128];
@ -952,7 +998,37 @@ int quit_cmd(unit * u, struct order *ord)
assert(kwd == K_QUIT); assert(kwd == K_QUIT);
passwd = gettoken(token, sizeof(token)); passwd = gettoken(token, sizeof(token));
if (checkpasswd(f, (const char *)passwd)) { if (checkpasswd(f, (const char *)passwd)) {
fset(f, FFL_QUIT); int flags = FFL_QUIT;
if (rule_transfermen()) {
param_t p;
p = getparam(f->locale);
if (p == P_FACTION) {
faction *f2 = getfaction();
if (f2 == NULL) {
cmistake(u, ord, 66, MSG_EVENT);
flags = 0;
}
else if (f->race != f2->race) {
cmistake(u, ord, 281, MSG_EVENT);
flags = 0;
}
else {
unit *u2;
for (u2 = u->region->units; u2; u2 = u2->next) {
if (u2->faction == f2 && ucontact(u2, u)) {
transfer_faction(u->faction, u2->faction);
break;
}
}
if (u2 == NULL) {
/* no target unit found */
cmistake(u, ord, 0, MSG_EVENT);
flags = 0;
}
}
}
}
f->flags |= flags;
} }
else { else {
char buffer[64]; char buffer[64];
@ -2077,7 +2153,7 @@ int banner_cmd(unit * u, struct order *ord)
init_order_depr(ord); init_order_depr(ord);
s = getstrtoken(); s = getstrtoken();
faction_setbanner(u->faction, s); faction_setbanner(u->faction, s);
add_message(&u->faction->msgs, msg_message("changebanner", "value", s)); ADDMSG(&u->faction->msgs, msg_message("changebanner", "value", s));
return 0; return 0;
} }

View File

@ -1,6 +1,7 @@
#include <platform.h> #include <platform.h>
#include "laws.h" #include "laws.h"
#include "battle.h" #include "battle.h"
#include "contact.h"
#include "guard.h" #include "guard.h"
#include "monsters.h" #include "monsters.h"
@ -1868,6 +1869,149 @@ static void test_long_order_on_ocean(CuTest *tc) {
test_teardown(); test_teardown();
} }
static void test_quit(CuTest *tc) {
faction *f;
unit *u;
region *r;
test_setup();
r = test_create_plain(0, 0);
f = test_create_faction(NULL);
u = test_create_unit(f, r);
u->thisorder = create_order(K_QUIT, f->locale, "password");
faction_setpassword(f, "passwort");
quit_cmd(u, u->thisorder);
CuAssertIntEquals(tc, 0, f->flags & FFL_QUIT);
faction_setpassword(f, "password");
quit_cmd(u, u->thisorder);
CuAssertIntEquals(tc, FFL_QUIT, f->flags & FFL_QUIT);
test_teardown();
}
/**
* Gifting units to another faction upon voluntary death (QUIT).
*/
static void test_quit_transfer(CuTest *tc) {
faction *f1, *f2;
unit *u1, *u2;
region *r;
test_setup();
r = test_create_plain(0, 0);
f1 = test_create_faction(NULL);
faction_setpassword(f1, "password");
u1 = test_create_unit(f1, r);
f2 = test_create_faction(NULL);
u2 = test_create_unit(f2, r);
contact_unit(u2, u1);
u1->thisorder = create_order(K_QUIT, f1->locale, "password %s %s",
LOC(f1->locale, parameters[P_FACTION]), itoa36(f2->no));
quit_cmd(u1, u1->thisorder);
CuAssertIntEquals(tc, FFL_QUIT, f1->flags & FFL_QUIT);
CuAssertPtrEquals(tc, f2, u1->faction);
test_teardown();
}
/**
* Gifting units with limited skills to another faction.
*
* This is allowed only up to the limit of the target faction.
* Units that would break the limit are not transferred.
*/
static void test_quit_transfer_limited(CuTest *tc) {
faction *f1, *f2;
unit *u1, *u2;
region *r;
test_setup();
r = test_create_plain(0, 0);
f1 = test_create_faction(NULL);
faction_setpassword(f1, "password");
u1 = test_create_unit(f1, r);
f2 = test_create_faction(NULL);
u2 = test_create_unit(f2, r);
contact_unit(u2, u1);
u1->thisorder = create_order(K_QUIT, f1->locale, "password %s %s",
LOC(f1->locale, parameters[P_FACTION]), itoa36(f2->no));
set_level(u1, SK_MAGIC, 1);
set_level(u2, SK_MAGIC, 1);
CuAssertIntEquals(tc, true, has_limited_skills(u1));
config_set_int("rules.maxskills.magic", 1);
quit_cmd(u1, u1->thisorder);
CuAssertIntEquals(tc, FFL_QUIT, f1->flags & FFL_QUIT);
CuAssertPtrEquals(tc, f1, u1->faction);
f1->flags -= FFL_QUIT;
config_set_int("rules.maxskills.magic", 2);
quit_cmd(u1, u1->thisorder);
CuAssertIntEquals(tc, FFL_QUIT, f1->flags & FFL_QUIT);
CuAssertPtrEquals(tc, f2, u1->faction);
test_teardown();
}
/**
* Only units of the same race can be gifted to another faction.
*/
static void test_quit_transfer_migrants(CuTest *tc) {
faction *f1, *f2;
unit *u1, *u2;
region *r;
test_setup();
r = test_create_plain(0, 0);
f1 = test_create_faction(NULL);
faction_setpassword(f1, "password");
u1 = test_create_unit(f1, r);
f2 = test_create_faction(NULL);
u2 = test_create_unit(f2, r);
contact_unit(u2, u1);
u1->thisorder = create_order(K_QUIT, f1->locale, "password %s %s",
LOC(f1->locale, parameters[P_FACTION]), itoa36(f2->no));
u_setrace(u1, test_create_race("smurf"));
quit_cmd(u1, u1->thisorder);
CuAssertIntEquals(tc, FFL_QUIT, f1->flags & FFL_QUIT);
CuAssertPtrEquals(tc, f1, u1->faction);
test_teardown();
}
/**
* A hero that is gifted to another faction loses their status.
*/
static void test_quit_transfer_hero(CuTest *tc) {
faction *f1, *f2;
unit *u1, *u2;
region *r;
test_setup();
r = test_create_plain(0, 0);
f1 = test_create_faction(NULL);
faction_setpassword(f1, "password");
u1 = test_create_unit(f1, r);
f2 = test_create_faction(NULL);
u2 = test_create_unit(f2, r);
contact_unit(u2, u1);
u1->thisorder = create_order(K_QUIT, f1->locale, "password %s %s",
LOC(f1->locale, parameters[P_FACTION]), itoa36(f2->no));
u1->flags |= UFL_HERO;
quit_cmd(u1, u1->thisorder);
CuAssertIntEquals(tc, FFL_QUIT, f1->flags & FFL_QUIT);
CuAssertPtrEquals(tc, f2, u1->faction);
CuAssertIntEquals(tc, 0, u1->flags & UFL_HERO);
test_teardown();
}
CuSuite *get_laws_suite(void) CuSuite *get_laws_suite(void)
{ {
CuSuite *suite = CuSuiteNew(); CuSuite *suite = CuSuiteNew();
@ -1943,6 +2087,11 @@ CuSuite *get_laws_suite(void)
SUITE_ADD_TEST(suite, test_nmr_timeout); SUITE_ADD_TEST(suite, test_nmr_timeout);
SUITE_ADD_TEST(suite, test_long_orders); SUITE_ADD_TEST(suite, test_long_orders);
SUITE_ADD_TEST(suite, test_long_order_on_ocean); SUITE_ADD_TEST(suite, test_long_order_on_ocean);
SUITE_ADD_TEST(suite, test_quit);
SUITE_ADD_TEST(suite, test_quit_transfer);
SUITE_ADD_TEST(suite, test_quit_transfer_limited);
SUITE_ADD_TEST(suite, test_quit_transfer_migrants);
SUITE_ADD_TEST(suite, test_quit_transfer_hero);
return suite; return suite;
} }

View File

@ -603,8 +603,7 @@ void plagues(region * r)
} }
if (dead > 0) { if (dead > 0) {
message *msg = add_message(&r->msgs, msg_message("pest", "dead", dead)); ADDMSG(&r->msgs, msg_message("pest", "dead", dead));
msg_release(msg);
deathcounts(r, dead); deathcounts(r, dead);
rsetpeasants(r, peasants - dead); rsetpeasants(r, peasants - dead);
} }

View File

@ -3109,8 +3109,8 @@ static int sp_chaossuction(castorder * co)
create_special_direction(rt, r, 2, "vortex_desc", "vortex", false); create_special_direction(rt, r, 2, "vortex_desc", "vortex", false);
new_border(&bt_chaosgate, r, rt); new_border(&bt_chaosgate, r, rt);
add_message(&r->msgs, msg_message("chaosgate_effect_1", "mage", caster)); ADDMSG(&r->msgs, msg_message("chaosgate_effect_1", "mage", caster));
add_message(&rt->msgs, msg_message("chaosgate_effect_2", "")); ADDMSG(&rt->msgs, msg_message("chaosgate_effect_2", ""));
return cast_level; return cast_level;
} }
@ -3450,7 +3450,7 @@ static bool can_charm(const unit * u, int maxlevel)
while (l < h) { while (l < h) {
int m = (l + h) / 2; int m = (l + h) / 2;
if (sk == expskills[m]) { if (sk == expskills[m]) {
if (skill_limit(u->faction, sk) != INT_MAX) { if (faction_skill_limit(u->faction, sk) != INT_MAX) {
return false; return false;
} }
else if ((int)sv->level > maxlevel) { else if ((int)sv->level > maxlevel) {

View File

@ -635,9 +635,9 @@ int study_cmd(unit * u, order * ord)
mtype = M_GRAY; mtype = M_GRAY;
} }
else if (!has_skill(u, SK_MAGIC)) { else if (!has_skill(u, SK_MAGIC)) {
int mmax = skill_limit(u->faction, SK_MAGIC); int mmax = faction_skill_limit(u->faction, SK_MAGIC);
/* Die Einheit ist noch kein Magier */ /* Die Einheit ist noch kein Magier */
if (count_skill(u->faction, SK_MAGIC) + u->number > mmax) { if (faction_count_skill(u->faction, SK_MAGIC) + u->number > mmax) {
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_max_magicians", ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_max_magicians",
"amount", mmax)); "amount", mmax));
return -1; return -1;
@ -694,8 +694,8 @@ int study_cmd(unit * u, order * ord)
if (sk == SK_ALCHEMY) { if (sk == SK_ALCHEMY) {
maxalchemy = effskill(u, SK_ALCHEMY, NULL); maxalchemy = effskill(u, SK_ALCHEMY, NULL);
if (!has_skill(u, SK_ALCHEMY)) { if (!has_skill(u, SK_ALCHEMY)) {
int amax = skill_limit(u->faction, SK_ALCHEMY); int amax = faction_skill_limit(u->faction, SK_ALCHEMY);
if (count_skill(u->faction, SK_ALCHEMY) + u->number > amax) { if (faction_count_skill(u->faction, SK_ALCHEMY) + u->number > amax) {
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_max_alchemists", ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_max_alchemists",
"amount", amax)); "amount", amax));
return -1; return -1;