From 1b3c8ea466807522883e80df374e205d47fe879f Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 24 Nov 2015 14:57:50 +0100 Subject: [PATCH 1/3] import monsters tests from PR #404 Monsters (i.e. sea serpents) can attack on oceans, when not guarding --- src/CMakeLists.txt | 1 + src/monsters.c | 2 +- src/monsters.test.c | 274 ++++++++++++++++++++++++++++++++++++++++++++ src/test_eressea.c | 1 + 4 files changed, 277 insertions(+), 1 deletion(-) create mode 100644 src/monsters.test.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 037898ed9..36c728d40 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -180,6 +180,7 @@ target_link_libraries(eressea ) set(TESTS_SRC + monsters.test.c donations.test.c wormhole.test.c alchemy.test.c diff --git a/src/monsters.c b/src/monsters.c index 3474c6b45..74321a2ea 100644 --- a/src/monsters.c +++ b/src/monsters.c @@ -789,7 +789,7 @@ void plan_monsters(faction * f) setstatus(u, ST_FIGHT); /* all monsters fight */ } - if (attacking && is_guard(u, GUARD_TAX)) { + if (attacking && (!r->land || is_guard(u, GUARD_TAX))) { monster_attacks(u); } /* units with a plan to kill get ATTACK orders: */ diff --git a/src/monsters.test.c b/src/monsters.test.c new file mode 100644 index 000000000..e3af8351d --- /dev/null +++ b/src/monsters.test.c @@ -0,0 +1,274 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "monster.h" +#include "guard.h" +#include "skill.h" + +#include + +#include +#include +#include +#include +#include + +extern void plan_monsters(struct faction *f); + +static void init_language(void) +{ + struct locale* lang; + int i; + + lang = get_or_create_locale("de"); + locale_setstring(lang, "skill::unarmed", "Waffenloser Kampf"); + locale_setstring(lang, "keyword::attack", "ATTACKIERE"); + locale_setstring(lang, "keyword::study", "LERNE"); + locale_setstring(lang, "keyword::tax", "TREIBE"); + locale_setstring(lang, "keyword::loot", "PLUENDERE"); + locale_setstring(lang, "keyword::guard", "BEWACHE"); + locale_setstring(lang, "keyword::move", "NACH"); + locale_setstring(lang, "keyword::message", "BOTSCHAFT"); + locale_setstring(lang, "REGION", "REGION"); + locale_setstring(lang, "east", "O"); + + for (i = 0; i < MAXKEYWORDS; ++i) { + if (!locale_getstring(lang, mkname("keyword", keywords[i]))) + locale_setstring(lang, mkname("keyword", keywords[i]), keywords[i]); + } + for (i = 0; i < MAXSKILLS; ++i) { + if (!locale_getstring(lang, mkname("skill", skillnames[i]))) + locale_setstring(lang, mkname("skill", skillnames[i]), skillnames[i]); + } + init_keywords(lang); + init_skills(lang); +} + +static order *find_order(const char *expected, const unit *unit) +{ + char cmd[32]; + order *order; + for (order = unit->orders; order; order = order->next) { + if (strcmp(expected, get_command(order, cmd, sizeof(cmd))) == 0) { + return order; + } + } + return NULL; +} + +static void create_monsters(faction **player, faction **monsters, region **r, unit **u, unit **m) { + race* rc; + + test_cleanup(); + + init_language(); + + test_create_world(); + *player = test_create_faction(NULL); + *monsters = get_or_create_monsters(); + assert(rc_find((*monsters)->race->_name)); + rc = rc_get_or_create((*monsters)->race->_name); + fset(rc, RCF_UNARMEDGUARD); + fset(rc, RCF_NPC); + fset(*monsters, FFL_NOIDLEOUT); + assert(fval(*monsters, FFL_NPC) && fval((*monsters)->race, RCF_UNARMEDGUARD) && fval((*monsters)->race, RCF_NPC) && fval(*monsters, FFL_NOIDLEOUT)); + (*monsters)->locale = default_locale; + + *r = findregion(0, 0); + + *u = test_create_unit(*player, *r); + unit_setid(*u, 1); + *m = test_create_unit(*monsters, *r); +} + +static void test_monsters_attack(CuTest * tc) +{ + faction *f, *f2; + region *r; + unit *u, *m; + + create_monsters(&f, &f2, &r, &u, &m); + + guard(m, GUARD_TAX); + + set_param(&global.parameters, "rules.monsters.attack_chance", "1"); + + plan_monsters(f2); + + CuAssertPtrNotNull(tc, find_order("ATTACKIERE 1", m)); + test_cleanup(); +} + +static void test_monsters_attack_ocean(CuTest * tc) +{ + faction *f, *f2; + region *r; + unit *u, *m; + + create_monsters(&f, &f2, &r, &u, &m); + r = findregion(-1, 0); + u = test_create_unit(u->faction, r); + unit_setid(u, 2); + m = test_create_unit(m->faction, r); + assert(!m->region->land); + + set_param(&global.parameters, "rules.monsters.attack_chance", "1"); + + plan_monsters(f2); + + CuAssertPtrNotNull(tc, find_order("ATTACKIERE 2", m)); + test_cleanup(); +} + +extern int monster_attacks(unit * monster, bool respect_buildings, bool rich_only); + +static void test_monsters_waiting(CuTest * tc) +{ + faction *f, *f2; + region *r; + unit *u, *m; + + create_monsters(&f, &f2, &r, &u, &m); + guard(m, GUARD_TAX); + fset(m, UFL_ISNEW); + // FIXME: monster_attacks(m, false, false); + CuAssertPtrEquals(tc, 0, find_order("ATTACKIERE 1", m)); + test_cleanup(); +} + +static void test_seaserpent_attack(CuTest * tc) +{ + faction *f, *f2; + region *r; + unit *u, *m; + race *rc; + + create_monsters(&f, &f2, &r, &u, &m); + r = findregion(-1, 0); + u = test_create_unit(u->faction, r); + unit_setid(u, 2); + m = test_create_unit(m->faction, r); + u_setrace(m, rc = test_create_race("seaserpent")); + assert(!m->region->land); + // FIXME: write an extra test for sea serpent piracy? + // fset(m, UFL_MOVED); + // fset(rc, RCF_ATTACK_MOVED); + + set_param(&global.parameters, "rules.monsters.attack_chance", "1"); + + plan_monsters(f2); + + CuAssertPtrNotNull(tc, find_order("ATTACKIERE 2", m)); + test_cleanup(); +} + +static void test_monsters_attack_not(CuTest * tc) +{ + faction *f, *f2; + region *r; + unit *u, *m; + + create_monsters(&f, &f2, &r, &u, &m); + + guard(m, GUARD_TAX); + guard(u, GUARD_TAX); + + set_param(&global.parameters, "rules.monsters.attack_chance", "0"); + + plan_monsters(f2); + + CuAssertPtrEquals(tc, 0, find_order("ATTACKIERE 1", m)); + test_cleanup(); +} + +static void test_dragon_attacks_the_rich(CuTest * tc) +{ + faction *f, *f2; + region *r; + unit *u, *m; + const item_type *i_silver; + + init_language(); + create_monsters(&f, &f2, &r, &u, &m); + + guard(m, GUARD_TAX); + set_level(m, SK_WEAPONLESS, 10); + + rsetmoney(r, 1); + rsetmoney(findregion(1, 0), 0); + i_silver = it_find("money"); + assert(i_silver); + i_change(&u->items, i_silver, 5000); + + set_param(&global.parameters, "rules.monsters.attack_chance", "0.00001"); + + plan_monsters(f2); + + CuAssertPtrNotNull(tc, find_order("ATTACKIERE 1", m)); + CuAssertPtrNotNull(tc, find_order("PLUENDERE", m)); + test_cleanup(); +} + +static void test_dragon_moves(CuTest * tc) +{ + faction *f, *f2; + region *r; + unit *u, *m; + + create_monsters(&f, &f2, &r, &u, &m); + rsetpeasants(r, 0); + rsetmoney(r, 0); + rsetmoney(findregion(1, 0), 1000); + + set_level(m, SK_WEAPONLESS, 10); + set_param(&global.parameters, "rules.monsters.attack_chance", ".0"); + plan_monsters(f2); + + CuAssertPtrNotNull(tc, find_order("NACH O", m)); + test_cleanup(); +} + +static void test_monsters_learn_exp(CuTest * tc) +{ + faction *f, *f2; + region *r; + unit *u, *m; + skill* sk; + + create_monsters(&f, &f2, &r, &u, &m); + set_param(&global.parameters, "study.from_use", "1"); + + u_setrace(u, u_race(m)); + produceexp(u, SK_MELEE, u->number); + sk = unit_skill(u, SK_MELEE); + CuAssertTrue(tc, !sk); + + produceexp(m, SK_MELEE, u->number); + sk = unit_skill(m, SK_MELEE); + CuAssertTrue(tc, sk && (sk->level > 0 || (sk->level == 0 && sk->weeks > 0))); + + test_cleanup(); +} + +CuSuite *get_monsters_suite(void) +{ + CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_monsters_attack); + SUITE_ADD_TEST(suite, test_monsters_attack_ocean); + SUITE_ADD_TEST(suite, test_seaserpent_attack); + DISABLE_TEST(suite, test_monsters_waiting); + SUITE_ADD_TEST(suite, test_monsters_attack_not); + SUITE_ADD_TEST(suite, test_dragon_attacks_the_rich); + DISABLE_TEST(suite, test_dragon_moves); + DISABLE_TEST(suite, test_monsters_learn_exp); + return suite; +} \ No newline at end of file diff --git a/src/test_eressea.c b/src/test_eressea.c index c04764992..da4f6e8d8 100644 --- a/src/test_eressea.c +++ b/src/test_eressea.c @@ -118,6 +118,7 @@ int RunAllTests(int argc, char *argv[]) ADD_SUITE(give); ADD_SUITE(laws); ADD_SUITE(market); + ADD_SUITE(monsters); ADD_SUITE(move); ADD_SUITE(piracy); ADD_SUITE(stealth); From bdc5372537e37b388a99ffad0687fdb99037ab0b Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 24 Nov 2015 16:12:33 +0100 Subject: [PATCH 2/3] disable test for seaserpent piracy (planned for 3.8) --- src/monsters.test.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/monsters.test.c b/src/monsters.test.c index e3af8351d..aba9d9c4d 100644 --- a/src/monsters.test.c +++ b/src/monsters.test.c @@ -35,6 +35,7 @@ static void init_language(void) locale_setstring(lang, "keyword::study", "LERNE"); locale_setstring(lang, "keyword::tax", "TREIBE"); locale_setstring(lang, "keyword::loot", "PLUENDERE"); + locale_setstring(lang, "keyword::piracy", "PIRATERIE"); locale_setstring(lang, "keyword::guard", "BEWACHE"); locale_setstring(lang, "keyword::move", "NACH"); locale_setstring(lang, "keyword::message", "BOTSCHAFT"); @@ -145,7 +146,7 @@ static void test_monsters_waiting(CuTest * tc) test_cleanup(); } -static void test_seaserpent_attack(CuTest * tc) +static void test_seaserpent_piracy(CuTest * tc) { faction *f, *f2; region *r; @@ -159,14 +160,14 @@ static void test_seaserpent_attack(CuTest * tc) m = test_create_unit(m->faction, r); u_setrace(m, rc = test_create_race("seaserpent")); assert(!m->region->land); - // FIXME: write an extra test for sea serpent piracy? - // fset(m, UFL_MOVED); + fset(m, UFL_MOVED); // fset(rc, RCF_ATTACK_MOVED); set_param(&global.parameters, "rules.monsters.attack_chance", "1"); plan_monsters(f2); + CuAssertPtrNotNull(tc, find_order("PIRATERIE", m)); CuAssertPtrNotNull(tc, find_order("ATTACKIERE 2", m)); test_cleanup(); } @@ -264,11 +265,11 @@ CuSuite *get_monsters_suite(void) CuSuite *suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_monsters_attack); SUITE_ADD_TEST(suite, test_monsters_attack_ocean); - SUITE_ADD_TEST(suite, test_seaserpent_attack); + DISABLE_TEST(suite, test_seaserpent_piracy); DISABLE_TEST(suite, test_monsters_waiting); SUITE_ADD_TEST(suite, test_monsters_attack_not); SUITE_ADD_TEST(suite, test_dragon_attacks_the_rich); DISABLE_TEST(suite, test_dragon_moves); DISABLE_TEST(suite, test_monsters_learn_exp); return suite; -} \ No newline at end of file +} From f301bec623bfd14b09c7e992a283b30a3781ccaf Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Wed, 25 Nov 2015 12:04:09 +0100 Subject: [PATCH 3/3] addressing comments by solthar on bug 2164, PR #408: storms should still cause damage to ships. cleaning up some configuration lookups in sail, moving them outside of loops. --- src/move.c | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/move.c b/src/move.c index 0cd847ed0..7780329cf 100644 --- a/src/move.c +++ b/src/move.c @@ -107,7 +107,7 @@ typedef struct follower { static void get_followers(unit * target, region * r, const region_list * route_end, -follower ** followers) + follower ** followers) { unit *uf; for (uf = r->units; uf; uf = uf->next) { @@ -211,7 +211,7 @@ static int eff_weight(const unit * u) static void get_transporters(const item * itm, int *p_animals, int *p_acap, int *p_vehicles, -int *p_vcap) + int *p_vcap) { int vehicles = 0, vcap = 0; int animals = 0, acap = 0; @@ -435,7 +435,7 @@ static int canride(unit * u) if (!(u_race(u)->flags & RCF_HORSE) && ((horses == 0 && unicorns == 0) - || horses > maxhorses || unicorns > maxunicorns)) { + || horses > maxhorses || unicorns > maxunicorns)) { return 0; } @@ -570,7 +570,7 @@ static void leave_trail(ship * sh, region * from, region_list * route) static void mark_travelthru(unit * u, region * r, const region_list * route, -const region_list * route_end) + const region_list * route_end) { /* kein travelthru in der letzten region! */ while (route != route_end) { @@ -1143,7 +1143,7 @@ static void cycle_route(order * ord, unit * u, int gereist) * hier keine normale direction), muss jede PAUSE einzeln * herausgefiltert und explizit gesetzt werden */ if (neworder != obuf) { - obuf += strlcat(obuf, " ", sizeof(neworder)-(obuf-neworder)); + obuf += strlcat(obuf, " ", sizeof(neworder) - (obuf - neworder)); } obuf += strlcat(obuf, LOC(lang, parameters[P_PAUSE]), sizeof(neworder) - (obuf - neworder)); } @@ -1273,8 +1273,8 @@ static bool roadto(const region * r, direction_t dir) region *r2; static const curse_type *roads_ct = NULL; - assert(r); - assert(dir= MAXDIRECTIONS || dir < 0) return false; r2 = rconnect(r, dir); @@ -1505,7 +1505,7 @@ static arg_regions *var_copy_regions(const region_list * begin, int size) if (size > 0) { int i = 0; - assert(size>0); + assert(size > 0); arg_regions *dst = (arg_regions *)malloc(sizeof(arg_regions) + sizeof(region *) * (size_t)size); dst->nregions = size; @@ -1779,6 +1779,9 @@ sail(unit * u, order * ord, bool move_on_land, region_list ** routep) faction *f = u->faction; region *next_point = NULL; int error; + double damage_storm = get_param_flt(global.parameters, "rules.ship.damage_storm", 0.02); + bool storms_enabled = get_param_int(global.parameters, "rules.ship.storms", 1) != 0; + int lighthouse_div = get_param_int(global.parameters, "rules.storm.lighthouse.divisor", 0); const char *token = getstrtoken(); if (routep) @@ -1830,7 +1833,7 @@ sail(unit * u, order * ord, bool move_on_land, region_list ** routep) if (!flying_ship(sh)) { 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; @@ -1840,9 +1843,8 @@ sail(unit * u, order * ord, bool move_on_land, region_list ** routep) /* 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; + if (lighthouse_div > 0) { + stormchance /= lighthouse_div; } else { stormchance = 0; @@ -1875,6 +1877,12 @@ sail(unit * u, order * ord, bool move_on_land, region_list ** routep) ADDMSG(&f->msgs, msg_message("storm", "ship region sink", sh, current_point, sh->damage >= sh->size * DAMAGE_SCALE)); + damage_ship(sh, damage_storm); + if (sh->damage >= sh->size * DAMAGE_SCALE) { + /* ship sinks, end journey here */ + break; + } + next_point = rnext; /* these values need to be updated if next_point changes (due to storms): */ tnext = next_point->terrain; @@ -1927,7 +1935,7 @@ sail(unit * u, order * ord, bool move_on_land, region_list ** routep) else { double dmg = get_param_flt(global.parameters, "rules.ship.damage.nolanding", - 0.10F); + 0.10F); ADDMSG(&f->msgs, msg_message("sailnolanding", "ship region", sh, next_point)); damage_ship(sh, dmg); @@ -2014,7 +2022,7 @@ sail(unit * u, order * ord, bool move_on_land, region_list ** routep) /* Das Schiff und alle Einheiten darin werden nun von * starting_point nach current_point verschoben */ - /* Verfolgungen melden */ + /* Verfolgungen melden */ if (fval(u, UFL_FOLLOWING)) caught_target(current_point, u); @@ -2051,7 +2059,7 @@ sail(unit * u, order * ord, bool move_on_land, region_list ** routep) if (trans) { message *msg = msg_message("harbor_trade", "unit items ship", harbourmaster, trans, - u->ship); + u->ship); add_message(&u->faction->msgs, msg); add_message(&harbourmaster->faction->msgs, msg); msg_release(msg); @@ -2134,7 +2142,7 @@ static const region_list *travel_i(unit * u, const region_list * route_begin, if (u2 == u) { const region_list *route_to = travel_route(ut, route_begin, route_end, ord, - TRAVEL_TRANSPORTED); + TRAVEL_TRANSPORTED); if (route_to != route_begin) { get_followers(ut, r, route_to, followers); @@ -2625,7 +2633,7 @@ void follow_unit(unit * u) if (id <= 0) { /* cmistake(u, ord, 20, MSG_MOVE); */ } - else { + else { ship *sh = findship(id); if (sh == NULL || (sh->region != r && hunted_dir(r->attribs, id) == NODIRECTION)) { cmistake(u, ord, 20, MSG_MOVE);