Merge pull request #313 from ennorehling/feature/module-piracy

Additional tests for NAME and PIRACY
This commit is contained in:
Enno Rehling 2015-10-13 15:52:31 +02:00
commit 8ba8d7408d
18 changed files with 585 additions and 264 deletions

View file

@ -79,6 +79,7 @@ ENDIF()
set (ERESSEA_SRC
calendar.c
move.c
piracy.c
spells.c
battle.c
alchemy.c
@ -200,6 +201,7 @@ set(TESTS_SRC
magic.test.c
market.test.c
move.test.c
piracy.test.c
prefix.test.c
skill.test.c
spells.test.c

View file

@ -4,7 +4,6 @@
#include "move.h"
#include <kernel/config.h>
#include <kernel/messages.h>
#include <kernel/faction.h>
#include <kernel/unit.h>
#include <kernel/race.h>
@ -39,16 +38,14 @@ static void test_herbsearch(CuTest * tc)
herbsearch(u, INT_MAX);
CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error59"));
free_messagelist(f->msgs);
f->msgs = 0;
test_clear_messages(f);
set_level(u, SK_HERBALISM, 1);
CuAssertPtrEquals(tc, u2, is_guarded(r, u, GUARD_PRODUCE));
herbsearch(u, INT_MAX);
CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error70"));
CuAssertPtrEquals(tc, 0, test_find_messagetype(f->msgs, "error59"));
free_messagelist(f->msgs);
f->msgs = 0;
test_clear_messages(f);
guard(u2, GUARD_NONE);
CuAssertPtrEquals(tc, 0, is_guarded(r, u, GUARD_PRODUCE));
@ -57,8 +54,7 @@ static void test_herbsearch(CuTest * tc)
CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error108"));
CuAssertPtrEquals(tc, 0, test_find_messagetype(f->msgs, "error70"));
CuAssertPtrEquals(tc, 0, test_find_messagetype(f->msgs, "error59"));
free_messagelist(f->msgs);
f->msgs = 0;
test_clear_messages(f);
rsetherbtype(r, itype);
CuAssertPtrEquals(tc, (void *)itype, (void *)rherbtype(r));
@ -68,8 +64,7 @@ static void test_herbsearch(CuTest * tc)
CuAssertPtrEquals(tc, 0, test_find_messagetype(f->msgs, "error108"));
CuAssertPtrEquals(tc, 0, test_find_messagetype(f->msgs, "error70"));
CuAssertPtrEquals(tc, 0, test_find_messagetype(f->msgs, "error59"));
free_messagelist(f->msgs);
f->msgs = 0;
test_clear_messages(f);
rsetherbs(r, 100);
CuAssertIntEquals(tc, 100, rherbs(r));
@ -81,8 +76,7 @@ static void test_herbsearch(CuTest * tc)
CuAssertPtrEquals(tc, 0, test_find_messagetype(f->msgs, "error108"));
CuAssertPtrEquals(tc, 0, test_find_messagetype(f->msgs, "error70"));
CuAssertPtrEquals(tc, 0, test_find_messagetype(f->msgs, "error59"));
free_messagelist(f->msgs);
f->msgs = 0;
test_clear_messages(f);
test_cleanup();
}

View file

@ -141,14 +141,12 @@ static struct unit *create_recruiter(void) {
static void test_heroes_dont_recruit(CuTest * tc) {
unit *u;
order *ord;
test_cleanup();
u = create_recruiter();
fset(u, UFL_HERO);
ord = create_order(K_RECRUIT, default_locale, "1");
unit_addorder(u, ord);
unit_addorder(u, create_order(K_RECRUIT, default_locale, "1"));
economics(u->region);
@ -160,13 +158,11 @@ static void test_heroes_dont_recruit(CuTest * tc) {
static void test_normals_recruit(CuTest * tc) {
unit *u;
order *ord;
test_cleanup();
u = create_recruiter();
ord = create_order(K_RECRUIT, default_locale, "1");
unit_addorder(u, ord);
unit_addorder(u, create_order(K_RECRUIT, default_locale, "1"));
economics(u->region);

View file

@ -7,7 +7,6 @@
#include <kernel/config.h>
#include <kernel/faction.h>
#include <kernel/item.h>
#include <kernel/messages.h>
#include <kernel/order.h>
#include <kernel/race.h>
#include <kernel/region.h>
@ -214,11 +213,11 @@ static void test_give_men_requires_contact(CuTest * tc) {
_snprintf(cmd, sizeof(cmd), "%s ALLES PERSONEN", itoa36(env.dst->no));
ord = create_order(K_GIVE, env.f1->locale, cmd);
free_messagelist(env.f1->msgs);
env.f1->msgs = 0;
test_clear_messages(env.f1);
give_cmd(env.src, ord);
CuAssertPtrEquals(tc, 0, test_find_messagetype(env.f1->msgs, "give_person"));
CuAssertPtrNotNull(tc, test_find_messagetype(env.f1->msgs, "feedback_no_contact"));
free_order(ord);
test_cleanup();
}
@ -292,6 +291,7 @@ static void test_give_herbs(CuTest * tc) {
give_cmd(env.src, ord);
CuAssertIntEquals(tc, 0, i_get(env.src->items, env.itype));
CuAssertIntEquals(tc, 10, i_get(env.dst->items, env.itype));
free_order(ord);
test_cleanup();
}
@ -344,6 +344,7 @@ static void test_give_invalid_target(CuTest *tc) {
give_cmd(env.src, ord);
CuAssertIntEquals(tc, 10, i_get(env.src->items, env.itype));
CuAssertPtrNotNull(tc, test_find_messagetype(env.f1->msgs, "feedback_unit_not_found"));
free_order(ord);
test_cleanup();
}

View file

@ -577,6 +577,7 @@ static void test_infinitive_from_config(CuTest *tc) {
ord = create_order(K_STUDY, lang, "");
CuAssertStrEquals(tc, "LERNE", get_command(ord, buffer, sizeof(buffer)));
free_order(ord);
test_cleanup();
}

View file

@ -123,6 +123,7 @@ static void test_init_order(CuTest *tc) {
CuAssertIntEquals(tc, K_MAKETEMP, init_order(ord));
CuAssertStrEquals(tc, "hurr", getstrtoken());
CuAssertStrEquals(tc, "durr", getstrtoken());
free_order(ord);
}
static void test_getstrtoken(CuTest *tc) {

View file

@ -35,6 +35,7 @@ static void test_infinitive(CuTest *tc) {
ord = create_order(K_STUDY, lang, "");
CuAssertStrEquals(tc, "LERNE", get_command(ord, buffer, sizeof(buffer)));
free_order(ord);
test_cleanup();
}

View file

@ -1622,6 +1622,7 @@ bool renamed_building(const building * b)
static int rename_cmd(unit * u, order * ord, char **s, const char *s2)
{
assert(s2);
if (!s2[0]) {
cmistake(u, ord, 84, MSG_EVENT);
return 0;
@ -1638,48 +1639,53 @@ static int rename_cmd(unit * u, order * ord, char **s, const char *s2)
return 0;
}
int
rename_building(unit * u, order * ord, building * b, const char *name)
{
static bool try_rename(unit *u, building *b, order *ord) {
unit *owner = b ? building_owner(b) : 0;
bool foreign = !(owner && owner->faction == u->faction);
if (!b) {
cmistake(u, ord, u->building ? 6 : 145, MSG_EVENT);
return -1;
return false;
}
if (!fval(b->type, BTF_NAMECHANGE) && renamed_building(b)) {
cmistake(u, ord, 278, MSG_EVENT);
return -1;
return false;
}
if (foreign) {
if (renamed_building(b)) {
cmistake(u, ord, 246, MSG_EVENT);
return -1;
return false;
}
if (owner) {
if (cansee(owner->faction, u->region, u, 0)) {
ADDMSG(&owner->faction->msgs,
msg_message("renamed_building_seen",
"building renamer region", b, u, u->region));
"building renamer region", b, u, u->region));
}
else {
ADDMSG(&owner->faction->msgs,
msg_message("renamed_building_notseen",
"building region", b, u->region));
"building region", b, u->region));
}
if (owner != u) {
cmistake(u, ord, 148, MSG_PRODUCE);
return false;
}
}
}
else {
if (owner != u) {
cmistake(u, ord, 148, MSG_PRODUCE);
return -1;
}
}
return true;
}
int
rename_building(unit * u, order * ord, building * b, const char *name)
{
assert(name);
if (!try_rename(u, b, ord)) {
return -1;
}
return rename_cmd(u, ord, &b->name, name);
}
@ -1718,9 +1724,10 @@ int name_cmd(struct unit *u, struct order *ord)
if (foreign) {
b = getbuilding(u->region);
}
return rename_building(u, ord, b, getstrtoken());
if (try_rename(u, b, ord)) {
s = &b->name;
}
break;
case P_FACTION:
if (foreign) {
faction *f;
@ -1896,6 +1903,9 @@ int name_cmd(struct unit *u, struct order *ord)
if (name) {
rename_cmd(u, ord, s, name);
}
else {
cmistake(u, ord, 84, MSG_EVENT);
}
}
return 0;

View file

@ -467,6 +467,7 @@ static void test_reserve_cmd(CuTest *tc) {
CuAssertIntEquals(tc, 200, reserve_cmd(u1, ord));
CuAssertIntEquals(tc, 200, i_get(u1->items, rtype->itype));
CuAssertIntEquals(tc, 0, i_get(u2->items, rtype->itype));
free_order(ord);
test_cleanup();
}
@ -517,6 +518,7 @@ static void test_pay_cmd(CuTest *tc) {
assert(ord);
CuAssertIntEquals(tc, 0, pay_cmd(fix.u1, ord));
CuAssertIntEquals(tc, BLD_DONTPAY, b->flags&BLD_DONTPAY);
free_order(ord);
test_cleanup();
}
@ -541,6 +543,7 @@ static void test_pay_cmd_other_building(CuTest *tc) {
CuAssertPtrEquals(tc, fix.u1, building_owner(b));
CuAssertIntEquals(tc, 0, pay_cmd(fix.u1, ord));
CuAssertIntEquals(tc, BLD_DONTPAY, b->flags&BLD_DONTPAY);
free_order(ord);
test_cleanup();
}
@ -559,6 +562,7 @@ static void test_pay_cmd_must_be_owner(CuTest *tc) {
assert(ord);
CuAssertIntEquals(tc, 0, pay_cmd(fix.u2, ord));
CuAssertIntEquals(tc, 0, b->flags&BLD_DONTPAY);
free_order(ord);
test_cleanup();
}
@ -566,8 +570,8 @@ static void test_new_units(CuTest *tc) {
unit *u;
faction *f;
region *r;
order *ord;
const struct locale *loc;
test_cleanup();
test_create_world();
f = test_create_faction(NULL);
@ -577,9 +581,7 @@ static void test_new_units(CuTest *tc) {
assert(u && !u->next);
loc = get_locale("de");
assert(loc);
ord = create_order(K_MAKETEMP, loc, "hurr");
assert(ord);
u->orders = ord;
u->orders = create_order(K_MAKETEMP, loc, "hurr");
new_units();
CuAssertPtrNotNull(tc, u->next);
test_cleanup();
@ -708,6 +710,7 @@ static void test_reserve_self(CuTest *tc) {
CuAssertIntEquals(tc, 100, reserve_self(u1, ord));
CuAssertIntEquals(tc, 100, i_get(u1->items, rtype->itype));
CuAssertIntEquals(tc, 100, i_get(u2->items, rtype->itype));
free_order(ord);
test_cleanup();
}
@ -765,17 +768,127 @@ static void test_luck_message(CuTest *tc) {
test_cleanup();
}
static unit * setup_name_cmd(void) {
faction *f;
struct locale *lang;
test_cleanup();
f = test_create_faction(0);
f->locale = lang = get_or_create_locale("en");
locale_setstring(lang, parameters[P_UNIT], "UNIT");
locale_setstring(lang, parameters[P_REGION], "REGION");
locale_setstring(lang, parameters[P_FACTION], "FACTION");
locale_setstring(lang, parameters[P_BUILDING], "BUILDING");
locale_setstring(lang, parameters[P_SHIP], "SHIP");
init_parameters(lang);
return test_create_unit(f, test_create_region(0, 0, 0));
}
static void test_name_unit(CuTest *tc) {
unit *u;
order *ord;
u = setup_name_cmd();
ord = create_order(K_NAME, u->faction->locale, "UNIT Hodor");
name_cmd(u, ord);
CuAssertStrEquals(tc, "Hodor", u->_name);
free_order(ord);
ord = create_order(K_NAME, u->faction->locale, "UNIT");
name_cmd(u, ord);
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error84"));
CuAssertStrEquals(tc, "Hodor", u->_name);
free_order(ord);
test_cleanup();
}
static void test_name_region(CuTest *tc) {
unit *u;
order *ord;
u = setup_name_cmd();
ord = create_order(K_NAME, u->faction->locale, "REGION Hodor");
name_cmd(u, ord);
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error145"));
u->building = test_create_building(u->region, 0);
name_cmd(u, ord);
CuAssertStrEquals(tc, "Hodor", u->region->land->name);
free_order(ord);
ord = create_order(K_NAME, u->faction->locale, "REGION");
name_cmd(u, ord);
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error84"));
CuAssertStrEquals(tc, "Hodor", u->region->land->name);
free_order(ord);
test_cleanup();
}
static void test_name_building(CuTest *tc) {
unit *u;
order *ord;
u = setup_name_cmd();
ord = create_order(K_NAME, u->faction->locale, "BUILDING Hodor");
name_cmd(u, ord);
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error145"));
u->building = test_create_building(u->region, 0);
name_cmd(u, ord);
CuAssertStrEquals(tc, "Hodor", u->building->name);
free_order(ord);
ord = create_order(K_NAME, u->faction->locale, "BUILDING");
name_cmd(u, ord);
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error84"));
CuAssertStrEquals(tc, "Hodor", u->building->name);
free_order(ord);
/* TODO: test BTF_NAMECHANGE:
btype->flags |= BTF_NAMECHANGE;
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error278"));
test_clear_messages(u->faction);
name_cmd(u, ord); */
test_cleanup();
}
static void test_name_ship(CuTest *tc) {
unit *u;
order *ord;
u = setup_name_cmd();
u->ship = test_create_ship(u->region, 0);
ord = create_order(K_NAME, u->faction->locale, "SHIP Hodor");
name_cmd(u, ord);
CuAssertStrEquals(tc, "Hodor", u->ship->name);
free_order(ord);
ord = create_order(K_NAME, u->faction->locale, "SHIP");
name_cmd(u, ord);
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error84"));
CuAssertStrEquals(tc, "Hodor", u->ship->name);
free_order(ord);
test_cleanup();
}
static void test_long_order_normal(CuTest *tc) {
// TODO: write more tests
unit *u;
order *ord;
test_cleanup();
u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0));
fset(u, UFL_MOVED);
fset(u, UFL_LONGACTION);
u->faction->locale = get_or_create_locale("de");
ord = create_order(K_MOVE, u->faction->locale, 0);
unit_addorder(u, ord);
unit_addorder(u, ord = create_order(K_MOVE, u->faction->locale, 0));
update_long_order(u);
CuAssertPtrEquals(tc, ord->data, u->thisorder->data);
CuAssertIntEquals(tc, 0, fval(u, UFL_MOVED));
@ -1011,6 +1124,7 @@ static void test_mail_unit(CuTest *tc) {
ord = create_order(K_MAIL, u->faction->locale, "EINHEIT %s 'Hodor!'", itoa36(u->no));
mail_cmd(u, ord);
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "unitmessage"));
free_order(ord);
test_cleanup();
}
@ -1022,6 +1136,7 @@ static void test_mail_faction(CuTest *tc) {
ord = create_order(K_MAIL, u->faction->locale, "PARTEI %s 'Hodor!'", itoa36(u->faction->no));
mail_cmd(u, ord);
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "regionmessage"));
free_order(ord);
test_cleanup();
}
@ -1033,6 +1148,7 @@ static void test_mail_region(CuTest *tc) {
ord = create_order(K_MAIL, u->faction->locale, "REGION 'Hodor!'", itoa36(u->no));
mail_cmd(u, ord);
CuAssertPtrNotNull(tc, test_find_messagetype(u->region->msgs, "mail_result"));
free_order(ord);
test_cleanup();
}
@ -1045,6 +1161,7 @@ static void test_mail_unit_no_msg(CuTest *tc) {
mail_cmd(u, ord);
CuAssertPtrEquals(tc, 0, test_find_messagetype(u->faction->msgs, "unitmessage"));
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error30"));
free_order(ord);
test_cleanup();
}
@ -1057,6 +1174,7 @@ static void test_mail_faction_no_msg(CuTest *tc) {
mail_cmd(u, ord);
CuAssertPtrEquals(tc, 0, test_find_messagetype(u->faction->msgs, "regionmessage"));
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error30"));
free_order(ord);
test_cleanup();
}
@ -1069,6 +1187,7 @@ static void test_mail_faction_no_target(CuTest *tc) {
mail_cmd(u, ord);
CuAssertPtrEquals(tc, 0, test_find_messagetype(u->faction->msgs, "regionmessage"));
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error66"));
free_order(ord);
test_cleanup();
}
@ -1081,6 +1200,7 @@ static void test_mail_region_no_msg(CuTest *tc) {
mail_cmd(u, ord);
CuAssertPtrEquals(tc, 0, test_find_messagetype(u->region->msgs, "mail_result"));
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error30"));
free_order(ord);
test_cleanup();
}
@ -1135,7 +1255,11 @@ CuSuite *get_laws_suite(void)
SUITE_ADD_TEST(suite, test_mail_faction_no_msg);
SUITE_ADD_TEST(suite, test_mail_region_no_msg);
SUITE_ADD_TEST(suite, test_mail_faction_no_target);
(void)test_luck_message; /* disabled, breaks on travis */
SUITE_ADD_TEST(suite, test_luck_message);
SUITE_ADD_TEST(suite, test_name_unit);
SUITE_ADD_TEST(suite, test_name_region);
SUITE_ADD_TEST(suite, test_name_building);
SUITE_ADD_TEST(suite, test_name_ship);
return suite;
}

View file

@ -27,6 +27,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "vortex.h"
#include "monster.h"
#include "lighthouse.h"
#include "piracy.h"
#include <kernel/build.h>
#include <kernel/building.h>
@ -55,7 +56,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <util/attrib.h>
#include <util/base36.h>
#include <util/bsdstring.h>
#include <util/goodies.h>
#include <util/language.h>
#include <util/lists.h>
#include <util/log.h>
@ -68,7 +68,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
/* attributes includes */
#include <attributes/follow.h>
#include <attributes/movement.h>
#include <attributes/otherfaction.h>
#include <attributes/stealth.h>
#include <attributes/targetregion.h>
@ -1720,15 +1719,15 @@ static bool ship_ready(const region * r, unit * u)
u->ship));
return false;
}
assert(u->ship->type->construction->improvement == NULL); /* sonst ist construction::size nicht ship_type::maxsize */
if (u->ship->size != u->ship->type->construction->maxsize) {
cmistake(u, u->thisorder, 15, MSG_MOVE);
return false;
if (u->ship->type->construction) {
assert(!u->ship->type->construction->improvement); /* sonst ist construction::size nicht ship_type::maxsize */
if (u->ship->size != u->ship->type->construction->maxsize) {
cmistake(u, u->thisorder, 15, MSG_MOVE);
return false;
}
}
if (!enoughsailors(u->ship, crew_skill(u->ship))) {
cmistake(u, u->thisorder, 1, MSG_MOVE);
/* mistake(u, u->thisorder,
"Auf dem Schiff befinden sich zuwenig erfahrene Seeleute.", MSG_MOVE); */
return false;
}
if (!cansail(r, u->ship)) {
@ -1770,7 +1769,7 @@ buildingtype_exists(const region * r, const building_type * bt, bool working)
/* Prüft, ob Ablegen von einer Küste in eine der erlaubten Richtungen erfolgt. */
static bool check_takeoff(ship * sh, region * from, region * to)
bool can_takeoff(const ship * sh, const region * from, const region * to)
{
if (!fval(from->terrain, SEA_REGION) && sh->coast != NODIRECTION) {
direction_t coast = sh->coast;
@ -1848,66 +1847,65 @@ sail(unit * u, order * ord, bool move_on_land, region_list ** routep)
}
if (!flying_ship(sh)) {
int stormchance;
int stormyness = 0;
int stormchance = 0;
int reason;
bool storms_enabled = get_param_int(global.parameters, "rules.ship.storms", 1) != 0;
if (storms_enabled) {
int stormyness;
gamedate date;
get_gamedate(turn, &date);
stormyness = storms ? storms[date.month] * 5 : 0;
}
/* storms should be the first thing we do. */
stormchance = stormyness / shipspeed(sh, u);
if (check_leuchtturm(next_point, NULL)) {
int param = get_param_int(global.parameters, "rules.lighthous.stormchancedevisor", 0);
if (param > 0) {
stormchance /= param;
/* storms should be the first thing we do. */
stormchance = stormyness / shipspeed(sh, u);
if (check_leuchtturm(next_point, NULL)) {
int param = get_param_int(global.parameters, "rules.lighthous.stormchancedevisor", 0);
if (param > 0) {
stormchance /= param;
}
else {
stormchance = 0;
}
}
else {
stormchance = 0;
}
}
if (rng_int() % 10000 < stormchance * sh->type->storm
&& fval(current_point->terrain, SEA_REGION)) {
if (!is_cursed(sh->attribs, C_SHIP_NODRIFT, 0)) {
region *rnext = NULL;
bool storm = true;
int d_offset = rng_int() % MAXDIRECTIONS;
direction_t d;
/* Sturm nur, wenn nächste Region Hochsee ist. */
for (d = 0; d != MAXDIRECTIONS; ++d) {
direction_t dnext = (direction_t)((d + d_offset) % MAXDIRECTIONS);
region *rn = rconnect(current_point, dnext);
if (rng_int() % 10000 < stormchance * sh->type->storm
&& fval(current_point->terrain, SEA_REGION)) {
if (!is_cursed(sh->attribs, C_SHIP_NODRIFT, 0)) {
region *rnext = NULL;
bool storm = true;
int d_offset = rng_int() % MAXDIRECTIONS;
direction_t d;
/* Sturm nur, wenn nächste Region Hochsee ist. */
for (d = 0; d != MAXDIRECTIONS; ++d) {
direction_t dnext = (direction_t)((d + d_offset) % MAXDIRECTIONS);
region *rn = rconnect(current_point, dnext);
if (rn != NULL) {
if (fval(rn->terrain, FORBIDDEN_REGION))
continue;
if (!fval(rn->terrain, SEA_REGION)) {
storm = false;
break;
if (rn != NULL) {
if (fval(rn->terrain, FORBIDDEN_REGION))
continue;
if (!fval(rn->terrain, SEA_REGION)) {
storm = false;
break;
}
if (rn != next_point)
rnext = rn;
}
if (rn != next_point)
rnext = rn;
}
if (storm && rnext != NULL) {
ADDMSG(&f->msgs, msg_message("storm", "ship region sink",
sh, current_point, sh->damage >= sh->size * DAMAGE_SCALE));
/* damage the ship. we handle destruction in the end */
damage_ship(sh, damage_drift());
if (sh->damage >= sh->size * DAMAGE_SCALE)
break;
next_point = rnext;
/* these values need to be updated if next_point changes (due to storms): */
tnext = next_point->terrain;
}
}
if (storm && rnext != NULL) {
ADDMSG(&f->msgs, msg_message("storm", "ship region sink",
sh, current_point, sh->damage >= sh->size * DAMAGE_SCALE));
/* damage the ship. we handle destruction in the end */
damage_ship(sh, damage_drift());
if (sh->damage >= sh->size * DAMAGE_SCALE)
break;
next_point = rnext;
/* these values need to be updated if next_point changes (due to storms): */
tnext = next_point->terrain;
}
}
}
} // storms_enabled
if (!fval(tthis, SEA_REGION)) {
if (!fval(tnext, SEA_REGION)) {
if (!move_on_land) {
@ -1918,7 +1916,7 @@ sail(unit * u, order * ord, bool move_on_land, region_list ** routep)
}
}
else {
if (!check_takeoff(sh, current_point, next_point)) {
if (!can_takeoff(sh, current_point, next_point)) {
/* Schiff kann nicht ablegen */
cmistake(u, ord, 182, MSG_MOVE);
break;
@ -2271,7 +2269,7 @@ static void travel(unit * u, region_list ** routep)
}
static void move(unit * u, bool move_on_land)
void move_cmd(unit * u, bool move_on_land)
{
region_list *route = NULL;
@ -2290,160 +2288,6 @@ static void move(unit * u, bool move_on_land)
free_regionlist(route);
}
typedef struct piracy_data {
const struct faction *pirate;
const struct faction *target;
direction_t dir;
} piracy_data;
static void piracy_init(struct attrib *a)
{
a->data.v = calloc(1, sizeof(piracy_data));
}
static void piracy_done(struct attrib *a)
{
free(a->data.v);
}
static attrib_type at_piracy_direction = {
"piracy_direction",
piracy_init,
piracy_done,
DEFAULT_AGE,
NO_WRITE,
NO_READ
};
static attrib *mk_piracy(const faction * pirate, const faction * target,
direction_t target_dir)
{
attrib *a = a_new(&at_piracy_direction);
piracy_data *data = a->data.v;
data->pirate = pirate;
data->target = target;
data->dir = target_dir;
return a;
}
static void piracy_cmd(unit * u, struct order *ord)
{
region *r = u->region;
ship *sh = u->ship, *sh2;
direction_t dir, target_dir = NODIRECTION;
struct {
const faction *target;
int value;
} aff[MAXDIRECTIONS];
int saff = 0;
int *il = NULL;
const char *s;
attrib *a;
if (!sh) {
cmistake(u, ord, 144, MSG_MOVE);
return;
}
if (!u->ship || u != ship_owner(u->ship)) {
cmistake(u, ord, 146, MSG_MOVE);
return;
}
/* Feststellen, ob schon ein anderer alliierter Pirat ein
* Ziel gefunden hat. */
init_order(ord);
s = getstrtoken();
if (s != NULL && *s) {
il = intlist_init();
while (s && *s) {
il = intlist_add(il, atoi36(s));
s = getstrtoken();
}
}
for (a = a_find(r->attribs, &at_piracy_direction);
a && a->type == &at_piracy_direction; a = a->next) {
piracy_data *data = a->data.v;
const faction *p = data->pirate;
const faction *t = data->target;
if (alliedunit(u, p, HELP_FIGHT)) {
if (il == 0 || (t && intlist_find(il, t->no))) {
target_dir = data->dir;
break;
}
}
}
/* Wenn nicht, sehen wir, ob wir ein Ziel finden. */
if (target_dir == NODIRECTION) {
/* Einheit ist also Kapitän. Jetzt gucken, in wievielen
* Nachbarregionen potentielle Opfer sind. */
for (dir = 0; dir < MAXDIRECTIONS; dir++) {
region *rc = rconnect(r, dir);
aff[dir].value = 0;
aff[dir].target = 0;
if (rc && fval(rc->terrain, SWIM_INTO) && check_takeoff(sh, r, rc)) {
for (sh2 = rc->ships; sh2; sh2 = sh2->next) {
unit *cap = ship_owner(sh2);
if (cap) {
faction *f = visible_faction(cap->faction, cap);
if (alliedunit(u, f, HELP_FIGHT))
continue;
if (il == 0 || intlist_find(il, cap->faction->no)) {
++aff[dir].value;
if (rng_int() % aff[dir].value == 0) {
aff[dir].target = f;
}
}
}
}
/* Und aufaddieren. */
saff += aff[dir].value;
}
}
if (saff != 0) {
saff = rng_int() % saff;
for (dir = 0; dir != MAXDIRECTIONS; ++dir) {
if (saff < aff[dir].value)
break;
saff -= aff[dir].value;
}
target_dir = dir;
a =
a_add(&r->attribs, mk_piracy(u->faction, aff[dir].target, target_dir));
}
}
free(il);
/* Wenn kein Ziel gefunden, entsprechende Meldung generieren */
if (target_dir == NODIRECTION) {
ADDMSG(&u->faction->msgs, msg_message("piratenovictim",
"ship region", sh, r));
return;
}
/* Meldung generieren */
ADDMSG(&u->faction->msgs, msg_message("piratesawvictim",
"ship region dir", sh, r, target_dir));
/* Befehl konstruieren */
set_order(&u->thisorder, create_order(K_MOVE, u->faction->locale, "%s",
LOC(u->faction->locale, directions[target_dir])));
/* Bewegung ausführen */
init_order(u->thisorder);
move(u, true);
}
static void age_traveldir(region * r)
{
attrib *a = a_find(r->attribs, &at_traveldir);
@ -2549,7 +2393,7 @@ static int follow_ship(unit * u, order * ord)
init_tokens_str(command);
getstrtoken();
/* NACH ausführen */
move(u, false);
move_cmd(u, false);
return 1; /* true -> Einheitenliste von vorne durchgehen */
}
@ -2656,7 +2500,7 @@ static void move_pirates(void)
/* else *up is already the next unit */
}
a_removeall(&r->attribs, &at_piracy_direction);
age_piracy(r);
age_traveldir(r);
}
}
@ -2716,13 +2560,13 @@ void movement(void)
if (ships) {
if (u->ship && ship_owner(u->ship) == u) {
init_order(u->thisorder);
move(u, false);
move_cmd(u, false);
}
}
else {
if (!u->ship || ship_owner(u->ship) != u) {
init_order(u->thisorder);
move(u, false);
move_cmd(u, false);
}
}
}

View file

@ -74,6 +74,8 @@ extern "C" {
const struct building_type *bt);
bool move_blocked(const struct unit *u, const struct region *src,
const struct region *dest);
bool can_takeoff(const struct ship * sh, const struct region * from, const struct region * to);
void move_cmd(struct unit * u, bool move_on_land);
#define SA_HARBOUR 2
#define SA_COAST 1

205
src/piracy.c Normal file
View file

@ -0,0 +1,205 @@
#include <platform.h>
#include <kernel/config.h>
#include "piracy.h"
#include "direction.h"
#include "keyword.h"
#include "move.h"
#include <kernel/faction.h>
#include <kernel/messages.h>
#include <kernel/order.h>
#include <kernel/region.h>
#include <kernel/ship.h>
#include <kernel/terrain.h>
#include <kernel/unit.h>
#include <util/attrib.h>
#include <util/base36.h>
#include <util/goodies.h>
#include <util/language.h>
#include <util/parser.h>
#include <util/rng.h>
#include <util/message.h>
#include <attributes/otherfaction.h>
#include <assert.h>
#include <stdlib.h>
typedef struct piracy_data {
const struct faction *pirate;
const struct faction *target;
direction_t dir;
} piracy_data;
static void piracy_init(struct attrib *a)
{
a->data.v = calloc(1, sizeof(piracy_data));
}
static void piracy_done(struct attrib *a)
{
free(a->data.v);
}
static attrib_type at_piracy_direction = {
"piracy_direction",
piracy_init,
piracy_done,
DEFAULT_AGE,
NO_WRITE,
NO_READ
};
static attrib *mk_piracy(const faction * pirate, const faction * target,
direction_t target_dir)
{
attrib *a = a_new(&at_piracy_direction);
piracy_data *data = a->data.v;
data->pirate = pirate;
data->target = target;
data->dir = target_dir;
return a;
}
static bool validate_pirate(unit *u, order *ord) {
if (!u->ship) {
cmistake(u, ord, 144, MSG_MOVE);
return false;
}
if (!u->ship || u != ship_owner(u->ship)) {
cmistake(u, ord, 146, MSG_MOVE);
return false;
}
return true;
}
int *parse_ids(const order *ord) {
const char *s;
int *il = NULL;
init_order(ord);
s = getstrtoken();
if (s != NULL && *s) {
il = intlist_init();
while (s && *s) {
il = intlist_add(il, atoi36(s));
s = getstrtoken();
}
}
return il;
}
direction_t find_piracy_target(unit *u, int *il) {
attrib *a;
region *r = u->region;
for (a = a_find(r->attribs, &at_piracy_direction);
a && a->type == &at_piracy_direction; a = a->next) {
piracy_data *data = a->data.v;
const faction *p = data->pirate;
const faction *t = data->target;
if (alliedunit(u, p, HELP_FIGHT)) {
if (il == 0 || (t && intlist_find(il, t->no))) {
return data->dir;
}
}
}
return NODIRECTION;
}
void piracy_cmd(unit * u, order *ord)
{
region *r = u->region;
ship *sh = u->ship, *sh2;
direction_t target_dir;
struct {
const faction *target;
int value;
} aff[MAXDIRECTIONS];
int saff = 0;
int *il;
if (!validate_pirate(u, ord)) {
return;
}
il = parse_ids(ord);
/* Feststellen, ob schon ein anderer alliierter Pirat ein
* Ziel gefunden hat. */
target_dir = find_piracy_target(u, il);
/* Wenn nicht, sehen wir, ob wir ein Ziel finden. */
if (target_dir == NODIRECTION) {
direction_t dir;
/* Einheit ist also Kapitän. Jetzt gucken, in wievielen
* Nachbarregionen potentielle Opfer sind. */
for (dir = 0; dir < MAXDIRECTIONS; dir++) {
region *rc = rconnect(r, dir);
aff[dir].value = 0;
aff[dir].target = 0;
if (rc && fval(rc->terrain, SAIL_INTO) && can_takeoff(sh, r, rc)) {
for (sh2 = rc->ships; sh2; sh2 = sh2->next) {
unit *cap = ship_owner(sh2);
if (cap) {
faction *f = visible_faction(cap->faction, cap);
if (alliedunit(u, f, HELP_FIGHT))
continue;
if (!il || intlist_find(il, cap->faction->no)) { // TODO: shouldn't this be f->no?
++aff[dir].value;
if (rng_int() % aff[dir].value == 0) {
aff[dir].target = f;
}
}
}
}
/* Und aufaddieren. */
saff += aff[dir].value;
}
}
if (saff != 0) {
saff = rng_int() % saff;
for (dir = 0; dir != MAXDIRECTIONS; ++dir) {
if (saff < aff[dir].value)
break;
saff -= aff[dir].value;
}
target_dir = dir;
a_add(&r->attribs, mk_piracy(u->faction, aff[dir].target, target_dir));
}
}
free(il);
/* Wenn kein Ziel gefunden, entsprechende Meldung generieren */
if (target_dir == NODIRECTION) {
ADDMSG(&u->faction->msgs, msg_message("piratenovictim",
"ship region", sh, r));
return;
}
/* Meldung generieren */
ADDMSG(&u->faction->msgs, msg_message("piratesawvictim",
"ship region dir", sh, r, target_dir));
/* Befehl konstruieren */
set_order(&u->thisorder, create_order(K_MOVE, u->faction->locale, "%s",
LOC(u->faction->locale, directions[target_dir])));
/* Bewegung ausführen */
init_order(u->thisorder);
move_cmd(u, true);
}
void age_piracy(region *r) {
a_removeall(&r->attribs, &at_piracy_direction);
}

21
src/piracy.h Normal file
View file

@ -0,0 +1,21 @@
#pragma once
#ifndef PIRACY_H
#define PIRACY_H
#ifdef __cplusplus
extern "C" {
#endif
struct unit;
struct order;
struct region;
void piracy_cmd(struct unit * u, struct order *ord);
void age_piracy(struct region *r);
#ifdef __cplusplus
}
#endif
#endif

109
src/piracy.test.c Normal file
View file

@ -0,0 +1,109 @@
#include <platform.h>
#include "piracy.h"
#include <kernel/config.h>
#include <kernel/region.h>
#include <kernel/unit.h>
#include <kernel/ship.h>
#include <kernel/terrain.h>
#include <kernel/faction.h>
#include <kernel/order.h>
#include <util/base36.h>
#include <util/language.h>
#include <CuTest.h>
#include <tests.h>
#include <assert.h>
static void setup_piracy(void) {
struct locale *lang;
terrain_type *t_ocean;
ship_type *st_boat;
test_cleanup();
set_param(&global.parameters, "rules.ship.storms", "0");
lang = get_or_create_locale("de");
locale_setstring(lang, directions[D_EAST], "OSTEN");
init_directions(lang);
t_ocean = test_create_terrain("ocean", SAIL_INTO | SEA_REGION);
st_boat = test_create_shiptype("boat");
st_boat->cargo = 1000;
}
static void test_piracy_cmd(CuTest * tc) {
faction *f;
region *r;
unit *u, *u2;
order *ord;
terrain_type *t_ocean;
ship_type *st_boat;
setup_piracy();
t_ocean = get_or_create_terrain("ocean");
st_boat = st_get_or_create("boat");
u2 = test_create_unit(test_create_faction(0), test_create_region(1, 0, t_ocean));
u_set_ship(u2, test_create_ship(u2->region, st_boat));
assert(u2);
u = test_create_unit(f = test_create_faction(0), r = test_create_region(0, 0, t_ocean));
set_level(u, SK_SAILING, st_boat->sumskill);
u_set_ship(u, test_create_ship(u->region, st_boat));
assert(f && u);
f->locale = get_or_create_locale("de");
ord = create_order(K_PIRACY, f->locale, "%s", itoa36(u2->faction->no));
assert(ord);
piracy_cmd(u, ord);
CuAssertPtrEquals(tc, 0, u->thisorder);
CuAssertTrue(tc, u->region != r);
CuAssertPtrEquals(tc, u2->region, u->region);
CuAssertPtrEquals(tc, u2->region, u->ship->region);
CuAssertPtrNotNullMsg(tc, "successful PIRACY sets attribute", r->attribs); // FIXME: this is testing implementation, not interface
CuAssertPtrNotNullMsg(tc, "successful PIRACY message", test_find_messagetype(f->msgs, "piratesawvictim"));
CuAssertPtrNotNullMsg(tc, "successful PIRACY movement", test_find_messagetype(f->msgs, "shipsail"));
free_order(ord);
test_cleanup();
}
static void test_piracy_cmd_errors(CuTest * tc) {
faction *f;
unit *u, *u2;
order *ord;
ship_type *st_boat;
setup_piracy();
st_boat = st_get_or_create("boat");
u = test_create_unit(f = test_create_faction(0), test_create_region(0, 0, get_or_create_terrain("ocean")));
f->locale = get_or_create_locale("de");
ord = create_order(K_PIRACY, f->locale, "");
assert(u && ord);
piracy_cmd(u, ord);
CuAssertPtrNotNullMsg(tc, "must be on a ship for PIRACY", test_find_messagetype(f->msgs, "error144"));
u_set_ship(u, test_create_ship(u->region, st_boat));
u2 = test_create_unit(u->faction, u->region);
u_set_ship(u2, u->ship);
test_clear_messages(f);
piracy_cmd(u2, ord);
CuAssertPtrNotNullMsg(tc, "must be owner for PIRACY", test_find_messagetype(f->msgs, "error146"));
test_clear_messages(f);
piracy_cmd(u, ord);
CuAssertPtrNotNullMsg(tc, "must specify target for PIRACY", test_find_messagetype(f->msgs, "piratenovictim"));
free_order(ord);
test_cleanup();
}
CuSuite *get_piracy_suite(void)
{
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_piracy_cmd_errors);
SUITE_ADD_TEST(suite, test_piracy_cmd);
return suite;
}

View file

@ -110,6 +110,7 @@ static void test_sabotage_self(CuTest *tc) {
assert(ord);
CuAssertIntEquals(tc, 0, sabotage_cmd(u, ord));
CuAssertPtrEquals(tc, 0, r->ships);
free_order(ord);
test_cleanup();
}
@ -139,6 +140,7 @@ static void test_sabotage_other_fail(CuTest *tc) {
msg = test_get_last_message(u->faction->msgs);
CuAssertStrEquals(tc, "destroy_ship_3", test_get_messagetype(msg));
CuAssertPtrNotNull(tc, r->ships);
free_order(ord);
test_cleanup();
}
@ -164,6 +166,7 @@ static void test_sabotage_other_success(CuTest *tc) {
set_level(u2, SK_SPY, 1);
CuAssertIntEquals(tc, 0, sabotage_cmd(u2, ord));
CuAssertPtrEquals(tc, 0, r->ships);
free_order(ord);
test_cleanup();
}

View file

@ -89,6 +89,7 @@ int RunAllTests(void)
RUN_TESTS(suite, laws);
RUN_TESTS(suite, market);
RUN_TESTS(suite, move);
RUN_TESTS(suite, piracy);
RUN_TESTS(suite, stealth);
RUN_TESTS(suite, upkeep);
RUN_TESTS(suite, vortex);

View file

@ -116,14 +116,14 @@ test_create_terrain(const char * name, unsigned int flags)
building * test_create_building(region * r, const building_type * btype)
{
building * b = new_building(btype ? btype : bt_get_or_create("castle"), r, default_locale);
building * b = new_building(btype ? btype : test_create_buildingtype("castle"), r, default_locale);
b->size = b->type->maxsize > 0 ? b->type->maxsize : 1;
return b;
}
ship * test_create_ship(region * r, const ship_type * stype)
{
ship * s = new_ship(stype ? stype : st_get_or_create("boat"), r, default_locale);
ship * s = new_ship(stype ? stype : test_create_shiptype("boat"), r, default_locale);
s->size = s->type->construction ? s->type->construction->maxsize : 1;
return s;
}
@ -134,6 +134,7 @@ ship_type * test_create_shiptype(const char * name)
stype->cptskill = 1;
stype->sumskill = 1;
stype->minskill = 1;
stype->range = 2;
if (!stype->construction) {
stype->construction = calloc(1, sizeof(construction));
stype->construction->maxsize = 5;
@ -149,7 +150,7 @@ ship_type * test_create_shiptype(const char * name)
building_type * test_create_buildingtype(const char * name)
{
building_type *btype = (building_type *)calloc(sizeof(building_type), 1);
building_type *btype = bt_get_or_create(name);
btype->flags = BTF_NAMECHANGE;
btype->_name = _strdup(name);
btype->construction = (construction *)calloc(sizeof(construction), 1);
@ -164,7 +165,6 @@ building_type * test_create_buildingtype(const char * name)
if (default_locale) {
locale_setstring(default_locale, name, name);
}
bt_register(btype);
return btype;
}
@ -279,6 +279,11 @@ struct message * test_find_messagetype(struct message_list *msgs, const char *na
return 0;
}
void test_clear_messages(faction *f) {
free_messagelist(f->msgs);
f->msgs = 0;
}
void disabled_test(void *suite, void (*test)(CuTest *), const char *name) {
(void)test;
fprintf(stderr, "%s: SKIP\n", name);

View file

@ -43,6 +43,7 @@ extern "C" {
const char * test_get_messagetype(const struct message *msg);
struct message * test_find_messagetype(struct message_list *msgs, const char *name);
struct message * test_get_last_message(struct message_list *mlist);
void test_clear_messages(struct faction *f);
void disabled_test(void *suite, void (*)(struct CuTest *), const char *name);