From 3a18384ddbb5588e38cbf7c2fd04bd94b9814652 Mon Sep 17 00:00:00 2001 From: Steffen Mecke Date: Sun, 15 Nov 2015 02:20:55 +0100 Subject: [PATCH 1/5] overfull ships do not drift but take damage http://bugs.eressea.de/view.php?id=2157 removed exploit: ships more than 2 times over their ship type's capacity do not drift but take 30% to 100% damage --- res/core/messages.xml | 7 ++ scripts/tests/e3/rules.lua | 24 ++++++ src/bind_ship.c | 17 +++++ src/kernel/ship.c | 2 +- src/move.c | 147 ++++++++++++++++++++++++------------- src/move.test.c | 104 ++++++++++++++++++++++++++ src/tests.c | 10 ++- src/tests.h | 2 + 8 files changed, 260 insertions(+), 53 deletions(-) diff --git a/res/core/messages.xml b/res/core/messages.xml index a3d7ad6e8..903550107 100644 --- a/res/core/messages.xml +++ b/res/core/messages.xml @@ -1615,6 +1615,13 @@ "Die $ship($ship) wird bei einer Kollision mit einem Eisberg beschädigt." "The $ship($ship) has been damaged by a collision with an iceberg." + + + + + "Die $ship($ship) ist zu stark überladen und wird stark beschädigt." + "The $ship($ship) is massively overloaded and is damaged heavily." + diff --git a/scripts/tests/e3/rules.lua b/scripts/tests/e3/rules.lua index 28b14fe10..b92df87b0 100644 --- a/scripts/tests/e3/rules.lua +++ b/scripts/tests/e3/rules.lua @@ -789,3 +789,27 @@ function test_volcanooutbreak_message() assert_not_equal("", msg:render("de")) assert_not_equal("", msg:render("en")) end + +function test_ship_massive() + set_rule("rules.ship.drifting", "1") + set_rule("rules.ship.damage_drift", "0.02") + local r = region.create(0,0, "ocean") + region.create(1,0, "plain") + region.create(0,1, "ocean") + local f = faction.create("massive@eressea.de", "human", "de") + + local s1 = ship.create(r, "cutter") + s1.damage = 390 + local u1 = unit.create(f, r, 10) + u1.ship = s1 +-- u1:set_skill("sailing", 10) + u1:clear_orders() + u1:add_order("NACH o") + + update_owners() + process_orders() + write_reports() + assert_equal(990, s1.damage) + + set_rule("rules.ship.drifting", "0") +end diff --git a/src/bind_ship.c b/src/bind_ship.c index 595be35dd..22015f673 100644 --- a/src/bind_ship.c +++ b/src/bind_ship.c @@ -192,6 +192,20 @@ static int tolua_ship_get_type(lua_State * L) return 1; } +static int tolua_ship_get_damage(lua_State * L) +{ + ship *self = (ship *)tolua_tousertype(L, 1, 0); + lua_pushinteger(L, self->damage); + return 1; +} + +static int tolua_ship_set_damage(lua_State * L) +{ + ship *self = (ship *)tolua_tousertype(L, 1, 0); + self->damage = (int)tolua_tonumber(L, 2, 0); + return 0; +} + void tolua_ship_open(lua_State * L) { /* register user types */ @@ -217,6 +231,9 @@ void tolua_ship_open(lua_State * L) tolua_variable(L, TOLUA_CAST "coast", tolua_ship_get_coast, tolua_ship_set_coast); tolua_variable(L, TOLUA_CAST "type", tolua_ship_get_type, 0); + tolua_variable(L, TOLUA_CAST "damage", tolua_ship_get_damage, + tolua_ship_set_damage); + #ifdef TODO .property("weight", &ship_getweight) .property("capacity", &ship_getcapacity) diff --git a/src/kernel/ship.c b/src/kernel/ship.c index c77a10bd4..b1c0e1725 100644 --- a/src/kernel/ship.c +++ b/src/kernel/ship.c @@ -172,7 +172,7 @@ struct ship *findshipr(const region * r, int n) void damage_ship(ship * sh, double percent) { double damage = - DAMAGE_SCALE * sh->type->damage * percent * sh->size + sh->damage; + DAMAGE_SCALE * sh->type->damage * percent * sh->size + sh->damage + .000001; sh->damage = (int)damage; } diff --git a/src/move.c b/src/move.c index 00752c3cf..ccb58ac9a 100644 --- a/src/move.c +++ b/src/move.c @@ -80,6 +80,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include int *storms; @@ -473,6 +474,27 @@ static bool cansail(const region * r, ship * sh) return true; } +static double overload(const region * r, ship * sh) +{ + /* sonst ist construction:: size nicht ship_type::maxsize */ + assert(!sh->type->construction + || sh->type->construction->improvement == NULL); + + if (sh->type->construction && sh->size != sh->type->construction->maxsize) { + return DBL_MAX; + } else { + int n = 0, p = 0; + int mcabins = sh->type->cabins; + + getshipweight(sh, &n, &p); + + double ovl = n / (double)sh->type->cargo; + if (mcabins) + ovl = _max(ovl, p / (double)mcabins); + return ovl; + } +} + int enoughsailors(const ship * sh, int crew_skill) { return crew_skill >= sh->type->sumskill; @@ -699,6 +721,37 @@ static float damage_drift(void) return value; } +static float damage_overload(void) +{ + static float value = -1.0F; + if (value < 0) { + value = (float)get_param_flt(global.parameters, "rules.ship.damage_overload", 0.3F); + } + return value; +} + +/* message to all factions in ship, start from firstu, end before lastu (may be NULL) */ +static void msg_to_ship_inmates(ship *sh, unit **firstu, unit **lastu, message *msg) { + unit *u, *shipfirst = NULL; + for (u = *firstu; u != *lastu; u = u->next) { + if (u->ship == sh) { + if (shipfirst == NULL) + shipfirst = u; + if (!fval(u->faction, FFL_MARK)) { + fset(u->faction, FFL_MARK); + add_message(&u->faction->msgs, msg); + } + *lastu = u->next; + } + } + if (shipfirst) + *firstu = shipfirst; + for (u = *firstu; u != *lastu; u = u->next) { + freset(u->faction, FFL_MARK); + } + msg_release(msg); +} + static void drifting_ships(region * r) { direction_t d; @@ -710,9 +763,10 @@ static void drifting_ships(region * r) ship *sh = *shp; region *rnext = NULL; region_list *route = NULL; - unit *firstu = NULL, *captain; + unit *firstu = r->units, *lastu = NULL, *captain; int d_offset; direction_t dir = 0; + double ovl; if (sh->type->fishing > 0) { sh->flags |= SF_FISHING; @@ -725,73 +779,64 @@ static void drifting_ships(region * r) } /* Kapitän bestimmen */ - for (captain = r->units; captain; captain = captain->next) { - if (captain->ship != sh) - continue; - if (firstu == NULL) - firstu = captain; - if (effskill(captain, SK_SAILING, r) >= sh->type->cptskill) { - break; - } - } + captain = ship_owner(sh); + if (effskill(captain, SK_SAILING, r) < sh->type->cptskill) + captain = NULL; + /* Kapitän da? Beschädigt? Genügend Matrosen? * Genügend leicht? Dann ist alles OK. */ - assert(sh->type->construction->improvement == NULL); /* sonst ist construction::size nicht ship_type::maxsize */ + assert(sh->type->construction->improvement == NULL); /* sonst ist construction::size nicht ship_type::maxsize */ if (captain && sh->size == sh->type->construction->maxsize && enoughsailors(sh, crew_skill(sh)) && cansail(r, sh)) { shp = &sh->next; continue; } - /* Auswahl einer Richtung: Zuerst auf Land, dann - * zufällig. Falls unmögliches Resultat: vergiß es. */ - d_offset = rng_int() % MAXDIRECTIONS; - for (d = 0; d != MAXDIRECTIONS; ++d) { - region *rn; - dir = (direction_t)((d + d_offset) % MAXDIRECTIONS); - rn = rconnect(r, dir); - if (rn != NULL && fval(rn->terrain, SAIL_INTO) && check_ship_allowed(sh, rn) > 0) { - rnext = rn; - if (!fval(rnext->terrain, SEA_REGION)) - break; - } - } - - if (rnext == NULL) { - shp = &sh->next; - continue; - } - - /* Das Schiff und alle Einheiten darin werden nun von r - * nach rnext verschoben. Danach eine Meldung. */ - add_regionlist(&route, rnext); - - set_coast(sh, r, rnext); - sh = move_ship(sh, r, rnext, route); - free_regionlist(route); - - if (firstu != NULL) { - unit *u, *lastu = NULL; - message *msg = msg_message("ship_drift", "ship dir", sh, dir); - for (u = firstu; u; u = u->next) { - if (u->ship == sh && !fval(u->faction, FFL_MARK)) { - fset(u->faction, FFL_MARK); - add_message(&u->faction->msgs, msg); - lastu = u->next; + ovl = overload(r, sh); + if (ovl >= 2) { + rnext = NULL; + } else { + /* Auswahl einer Richtung: Zuerst auf Land, dann + * zufällig. Falls unmögliches Resultat: vergiß es. */ + d_offset = rng_int () % MAXDIRECTIONS; + for (d = 0; d != MAXDIRECTIONS; ++d) { + region *rn; + dir = (direction_t)((d + d_offset) % MAXDIRECTIONS); + rn = rconnect(r, dir); + if (rn != NULL && fval(rn->terrain, SAIL_INTO) && check_ship_allowed(sh, rn) > 0) { + rnext = rn; + if (!fval(rnext->terrain, SEA_REGION)) + break; } } - for (u = firstu; u != lastu; u = u->next) { - freset(u->faction, FFL_MARK); + } + + if (rnext != NULL) { + + /* Das Schiff und alle Einheiten darin werden nun von r + * nach rnext verschoben. Danach eine Meldung. */ + add_regionlist(&route, rnext); + + set_coast(sh, r, rnext); + sh = move_ship(sh, r, rnext, route); + free_regionlist(route); + + if (firstu != NULL) { + message *msg = msg_message("ship_drift", "ship dir", sh, dir); + msg_to_ship_inmates(sh, &firstu, &lastu, msg); } - msg_release(msg); } if (sh != NULL) { fset(sh, SF_DRIFTED); - - damage_ship(sh, damage_drift()); + if (ovl >= 2) { + damage_ship(sh, (ovl - 1) * damage_overload()); + msg_to_ship_inmates(sh, &firstu, &lastu, msg_message("massive_overload", "ship", sh)); + } else + damage_ship(sh, damage_drift()); if (sh->damage >= sh->size * DAMAGE_SCALE) { + msg_to_ship_inmates(sh, &firstu, &lastu, msg_message("shipsink", "ship", sh)); remove_ship(&sh->region->ships, sh); } } diff --git a/src/move.test.c b/src/move.test.c index 5c24ca72e..d0a91b9b9 100644 --- a/src/move.test.c +++ b/src/move.test.c @@ -295,6 +295,104 @@ static void test_age_trails(CuTest *tc) { test_cleanup(); } +struct drift_fixture { + faction *f; + region *r; + unit *u; + terrain_type *t_ocean; + ship_type *st_boat; + struct locale *lang; + ship *sh; + +}; + +void setup_drift (struct drift_fixture *fix) { + test_cleanup(); + set_param(&global.parameters, "rules.ship.storms", "0"); + fix->lang = get_or_create_locale("de"); + + test_create_world(); + + fix->st_boat = st_get_or_create("boat"); + fix->st_boat->cabins = 2000; + fix->u = test_create_unit(fix->f = test_create_faction(0), fix->r=findregion(-1,0)); + assert(fix->r && fix->r->terrain->flags & SAIL_INTO); + set_level(fix->u, SK_SAILING, fix->st_boat->sumskill); + u_set_ship(fix->u, fix->sh = test_create_ship(fix->u->region, fix->st_boat)); + assert(fix->f && fix->u && fix->sh); + fix->f->locale = get_or_create_locale("de"); + +} + +static void test_ship_no_overload(CuTest *tc) { + struct drift_fixture fix; + setup_drift(&fix); + + fix.u->number = 2; + movement(); + CuAssertPtrEquals(tc, fix.u->region, findregion(-1,0)); + CuAssertIntEquals(tc, 0, fix.sh->damage); +} + +static void test_ship_normal_overload(CuTest *tc) { + struct drift_fixture fix; + setup_drift(&fix); + + fix.u->number = 3; + movement(); + CuAssertPtrEquals(tc, fix.u->region, findregion(0, 0)); + CuAssertDblEquals(tc, (double) (fix.sh->size * DAMAGE_SCALE * .02), (double ) fix.sh->damage, ASSERT_DBL_DELTA); + CuAssertPtrNotNull(tc, test_find_messagetype(fix.f->msgs, "ship_drift")); +} + +static void test_ship_big_overload(CuTest *tc) { + struct drift_fixture fix; + setup_drift(&fix); + + fix.u->number = 4; + movement(); + CuAssertPtrEquals(tc, fix.u->region, findregion(-1, 0)); + CuAssertDblEquals(tc, (double) (fix.sh->size * DAMAGE_SCALE * .3), (double ) fix.sh->damage, ASSERT_DBL_DELTA); + CuAssertPtrNotNull(tc, test_find_messagetype(fix.f->msgs, "massive_overload")); +} + +static void test_ship_no_real_overload(CuTest *tc) { + struct drift_fixture fix; + setup_drift(&fix); + + fix.u->number = 3; + damage_ship(fix.sh, .80); + movement(); + CuAssertPtrEquals(tc, fix.u->region, findregion(0, 0)); + CuAssertDblEquals(tc, (double) (fix.sh->size * DAMAGE_SCALE * .82), (double ) fix.sh->damage, ASSERT_DBL_DELTA); + CuAssertPtrEquals(tc, 0, test_find_messagetype(fix.f->msgs, "massive_overload")); +} + +static void test_ship_ridiculous_overload(CuTest *tc) { + struct drift_fixture fix; + setup_drift(&fix); + + fix.u->number = 100; + movement(); + CuAssertTrue(tc, fix.sh->size * DAMAGE_SCALE * .2 < fix.sh->damage); + CuAssertPtrEquals(tc, 0, fix.sh->region); + CuAssertPtrNotNull(tc, test_find_messagetype(fix.f->msgs, "massive_overload")); + CuAssertPtrNotNull(tc, test_find_messagetype(fix.f->msgs, "shipsink")); +} + +static void test_ship_ridiculous_overload_no_captain(CuTest *tc) { + struct drift_fixture fix; + setup_drift(&fix); + set_level(fix.u, SK_SAILING, 0); + + fix.u->number = 100; + movement(); + CuAssertTrue(tc, fix.sh->size * DAMAGE_SCALE * .2 < fix.sh->damage); + CuAssertPtrEquals(tc, 0, fix.sh->region); + CuAssertPtrNotNull(tc, test_find_messagetype(fix.f->msgs, "massive_overload")); + CuAssertPtrNotNull(tc, test_find_messagetype(fix.f->msgs, "shipsink")); +} + CuSuite *get_move_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -309,5 +407,11 @@ CuSuite *get_move_suite(void) SUITE_ADD_TEST(suite, test_is_guarded); SUITE_ADD_TEST(suite, test_ship_trails); SUITE_ADD_TEST(suite, test_age_trails); + SUITE_ADD_TEST(suite, test_ship_no_overload); + SUITE_ADD_TEST(suite, test_ship_normal_overload); + SUITE_ADD_TEST(suite, test_ship_no_real_overload); + SUITE_ADD_TEST(suite, test_ship_big_overload); + SUITE_ADD_TEST(suite, test_ship_ridiculous_overload); + SUITE_ADD_TEST(suite, test_ship_ridiculous_overload_no_captain); return suite; } diff --git a/src/tests.c b/src/tests.c index e7518211e..264416458 100644 --- a/src/tests.c +++ b/src/tests.c @@ -135,6 +135,8 @@ ship_type * test_create_shiptype(const char * name) stype->sumskill = 1; stype->minskill = 1; stype->range = 2; + stype->cargo = 1000; + stype->damage = 1; if (!stype->construction) { stype->construction = calloc(1, sizeof(construction)); stype->construction->maxsize = 5; @@ -142,6 +144,12 @@ ship_type * test_create_shiptype(const char * name) stype->construction->reqsize = 1; stype->construction->skill = SK_SHIPBUILDING; } + + stype->coasts = + (terrain_type **)malloc(sizeof(terrain_type *)*2); + stype->coasts[0] = get_or_create_terrain("plain"); + stype->coasts[1] = NULL; + if (default_locale) { locale_setstring(default_locale, name, name); } @@ -238,7 +246,7 @@ void test_create_world(void) test_create_itemtype("iron"); test_create_itemtype("stone"); - t_plain = test_create_terrain("plain", LAND_REGION | FOREST_REGION | WALK_INTO | CAVALRY_REGION); + t_plain = test_create_terrain("plain", LAND_REGION | FOREST_REGION | WALK_INTO | CAVALRY_REGION | SAIL_INTO); t_plain->size = 1000; t_plain->max_road = 100; t_ocean = test_create_terrain("ocean", SEA_REGION | SAIL_INTO | SWIM_INTO); diff --git a/src/tests.h b/src/tests.h index f0b64729b..b69b5ff0e 100644 --- a/src/tests.h +++ b/src/tests.h @@ -8,6 +8,8 @@ extern "C" { #endif + #define ASSERT_DBL_DELTA 0.001 + struct region; struct unit; struct faction; From a7a82662b7598099097ba3fe14e83f60f7170ed3 Mon Sep 17 00:00:00 2001 From: Steffen Mecke Date: Tue, 17 Nov 2015 15:47:43 +0100 Subject: [PATCH 2/5] reduce maximum overload damage to 38% --- src/kernel/ship.c | 4 ++++ src/kernel/ship.h | 2 ++ src/move.c | 18 +++++++++--------- src/move.h | 2 ++ src/move.test.c | 35 +++++++++++++++++++++++------------ src/report.c | 3 +-- 6 files changed, 41 insertions(+), 23 deletions(-) diff --git a/src/kernel/ship.c b/src/kernel/ship.c index b1c0e1725..40198068a 100644 --- a/src/kernel/ship.c +++ b/src/kernel/ship.c @@ -469,3 +469,7 @@ const char *ship_getname(const ship * self) { return self->name; } + +int ship_damage_percent(const ship *ship) { + return (ship->damage * 100 + DAMAGE_SCALE - 1) / (ship->size * DAMAGE_SCALE); +} diff --git a/src/kernel/ship.h b/src/kernel/ship.h index 6ba2de75d..73b8a9718 100644 --- a/src/kernel/ship.h +++ b/src/kernel/ship.h @@ -126,6 +126,8 @@ extern "C" { void ship_setname(struct ship *self, const char *name); int shipspeed(const struct ship *sh, const struct unit *u); int crew_skill(const struct ship *sh); + + int ship_damage_percent(const struct ship *ship); #ifdef __cplusplus } #endif diff --git a/src/move.c b/src/move.c index ccb58ac9a..26b536aed 100644 --- a/src/move.c +++ b/src/move.c @@ -721,13 +721,13 @@ static float damage_drift(void) return value; } -static float damage_overload(void) +static double damage_overload(double overload) { - static float value = -1.0F; - if (value < 0) { - value = (float)get_param_flt(global.parameters, "rules.ship.damage_overload", 0.3F); - } - return value; + static double damage; + damage = get_param_flt(global.parameters, "rules.ship.damage_overload_min", 0.2); + damage *= overload - OVERLOAD_THRESHOLD + 1; + damage = _min(damage, get_param_flt(global.parameters, "rules.ship.damage_overload_max", 0.37)); + return damage; } /* message to all factions in ship, start from firstu, end before lastu (may be NULL) */ @@ -794,7 +794,7 @@ static void drifting_ships(region * r) } ovl = overload(r, sh); - if (ovl >= 2) { + if (ovl >= OVERLOAD_THRESHOLD) { rnext = NULL; } else { /* Auswahl einer Richtung: Zuerst auf Land, dann @@ -830,8 +830,8 @@ static void drifting_ships(region * r) if (sh != NULL) { fset(sh, SF_DRIFTED); - if (ovl >= 2) { - damage_ship(sh, (ovl - 1) * damage_overload()); + if (ovl >= OVERLOAD_THRESHOLD) { + damage_ship(sh, damage_overload(ovl)); msg_to_ship_inmates(sh, &firstu, &lastu, msg_message("massive_overload", "ship", sh)); } else damage_ship(sh, damage_drift()); diff --git a/src/move.h b/src/move.h index c0b92d071..2f93bcd0a 100644 --- a/src/move.h +++ b/src/move.h @@ -42,6 +42,8 @@ extern "C" { #define HORSESNEEDED 2 #define STRENGTHMULTIPLIER 50 /* multiplier for trollbelt */ +#define OVERLOAD_THRESHOLD 1.5 + /* ein mensch wiegt 10, traegt also 5, ein pferd wiegt 50, traegt also 20. ein ** wagen wird von zwei pferden gezogen und traegt total 140, davon 40 die ** pferde, macht nur noch 100, aber samt eigenem gewicht (40) macht also 140. */ diff --git a/src/move.test.c b/src/move.test.c index d0a91b9b9..f1aefa34b 100644 --- a/src/move.test.c +++ b/src/move.test.c @@ -314,7 +314,7 @@ void setup_drift (struct drift_fixture *fix) { test_create_world(); fix->st_boat = st_get_or_create("boat"); - fix->st_boat->cabins = 2000; + fix->st_boat->cabins = 10000; fix->u = test_create_unit(fix->f = test_create_faction(0), fix->r=findregion(-1,0)); assert(fix->r && fix->r->terrain->flags & SAIL_INTO); set_level(fix->u, SK_SAILING, fix->st_boat->sumskill); @@ -338,10 +338,10 @@ static void test_ship_normal_overload(CuTest *tc) { struct drift_fixture fix; setup_drift(&fix); - fix.u->number = 3; + fix.u->number = 12; movement(); CuAssertPtrEquals(tc, fix.u->region, findregion(0, 0)); - CuAssertDblEquals(tc, (double) (fix.sh->size * DAMAGE_SCALE * .02), (double ) fix.sh->damage, ASSERT_DBL_DELTA); + CuAssertIntEquals(tc, 2, ship_damage_percent(fix.sh)); CuAssertPtrNotNull(tc, test_find_messagetype(fix.f->msgs, "ship_drift")); } @@ -349,10 +349,10 @@ static void test_ship_big_overload(CuTest *tc) { struct drift_fixture fix; setup_drift(&fix); - fix.u->number = 4; + fix.u->number = 15; movement(); CuAssertPtrEquals(tc, fix.u->region, findregion(-1, 0)); - CuAssertDblEquals(tc, (double) (fix.sh->size * DAMAGE_SCALE * .3), (double ) fix.sh->damage, ASSERT_DBL_DELTA); + CuAssertIntEquals(tc, 20, ship_damage_percent(fix.sh)); CuAssertPtrNotNull(tc, test_find_messagetype(fix.f->msgs, "massive_overload")); } @@ -360,11 +360,11 @@ static void test_ship_no_real_overload(CuTest *tc) { struct drift_fixture fix; setup_drift(&fix); - fix.u->number = 3; + fix.u->number = 12; damage_ship(fix.sh, .80); movement(); CuAssertPtrEquals(tc, fix.u->region, findregion(0, 0)); - CuAssertDblEquals(tc, (double) (fix.sh->size * DAMAGE_SCALE * .82), (double ) fix.sh->damage, ASSERT_DBL_DELTA); + CuAssertIntEquals(tc, 82, ship_damage_percent(fix.sh)); CuAssertPtrEquals(tc, 0, test_find_messagetype(fix.f->msgs, "massive_overload")); } @@ -374,10 +374,8 @@ static void test_ship_ridiculous_overload(CuTest *tc) { fix.u->number = 100; movement(); - CuAssertTrue(tc, fix.sh->size * DAMAGE_SCALE * .2 < fix.sh->damage); - CuAssertPtrEquals(tc, 0, fix.sh->region); + CuAssertIntEquals(tc, 37, ship_damage_percent(fix.sh)); CuAssertPtrNotNull(tc, test_find_messagetype(fix.f->msgs, "massive_overload")); - CuAssertPtrNotNull(tc, test_find_messagetype(fix.f->msgs, "shipsink")); } static void test_ship_ridiculous_overload_no_captain(CuTest *tc) { @@ -387,12 +385,24 @@ static void test_ship_ridiculous_overload_no_captain(CuTest *tc) { fix.u->number = 100; movement(); - CuAssertTrue(tc, fix.sh->size * DAMAGE_SCALE * .2 < fix.sh->damage); - CuAssertPtrEquals(tc, 0, fix.sh->region); + CuAssertIntEquals(tc, 37, ship_damage_percent(fix.sh)); CuAssertPtrNotNull(tc, test_find_messagetype(fix.f->msgs, "massive_overload")); +} + +static void test_ship_ridiculous_overload_bad(CuTest *tc) { + struct drift_fixture fix; + setup_drift(&fix); + + fix.u->number = 200; + set_param(&global.parameters, "rules.ship.damage_overload_max", "1"); + movement(); + CuAssertTrue(tc, ship_damage_percent(fix.sh) > 99); + CuAssertPtrNotNull(tc, test_find_messagetype(fix.f->msgs, "massive_overload")); + CuAssertPtrEquals(tc, 0, fix.sh->region); CuAssertPtrNotNull(tc, test_find_messagetype(fix.f->msgs, "shipsink")); } + CuSuite *get_move_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -412,6 +422,7 @@ CuSuite *get_move_suite(void) SUITE_ADD_TEST(suite, test_ship_no_real_overload); SUITE_ADD_TEST(suite, test_ship_big_overload); SUITE_ADD_TEST(suite, test_ship_ridiculous_overload); + SUITE_ADD_TEST(suite, test_ship_ridiculous_overload_bad); SUITE_ADD_TEST(suite, test_ship_ridiculous_overload_no_captain); return suite; } diff --git a/src/report.c b/src/report.c index f157a75f4..291356b40 100644 --- a/src/report.c +++ b/src/report.c @@ -1804,8 +1804,7 @@ const unit * captain) WARN_STATIC_BUFFER(); } if (sh->damage) { - int percent = - (sh->damage * 100 + DAMAGE_SCALE - 1) / (sh->size * DAMAGE_SCALE); + int percent = ship_damage_percent(sh); bytes = _snprintf(bufp, size, ", %d%% %s", percent, LOC(f->locale, "nr_damaged")); if (wrptr(&bufp, &size, bytes) != 0) From 92ee0f24a485315da6afafd47fbfb613692eb478 Mon Sep 17 00:00:00 2001 From: Steffen Mecke Date: Thu, 19 Nov 2015 12:36:15 +0100 Subject: [PATCH 3/5] tweaked parameters for massive overload so ... many ... configuration parameters scrubbed lua test --- scripts/tests/e3/rules.lua | 24 ----------------------- src/move.c | 40 +++++++++++++++++++++++++++++++------- src/move.h | 2 -- src/move.test.c | 28 +++++++++++++++++--------- 4 files changed, 52 insertions(+), 42 deletions(-) diff --git a/scripts/tests/e3/rules.lua b/scripts/tests/e3/rules.lua index b92df87b0..28b14fe10 100644 --- a/scripts/tests/e3/rules.lua +++ b/scripts/tests/e3/rules.lua @@ -789,27 +789,3 @@ function test_volcanooutbreak_message() assert_not_equal("", msg:render("de")) assert_not_equal("", msg:render("en")) end - -function test_ship_massive() - set_rule("rules.ship.drifting", "1") - set_rule("rules.ship.damage_drift", "0.02") - local r = region.create(0,0, "ocean") - region.create(1,0, "plain") - region.create(0,1, "ocean") - local f = faction.create("massive@eressea.de", "human", "de") - - local s1 = ship.create(r, "cutter") - s1.damage = 390 - local u1 = unit.create(f, r, 10) - u1.ship = s1 --- u1:set_skill("sailing", 10) - u1:clear_orders() - u1:add_order("NACH o") - - update_owners() - process_orders() - write_reports() - assert_equal(990, s1.damage) - - set_rule("rules.ship.drifting", "0") -end diff --git a/src/move.c b/src/move.c index 26b536aed..0ec4940c6 100644 --- a/src/move.c +++ b/src/move.c @@ -721,12 +721,38 @@ static float damage_drift(void) return value; } -static double damage_overload(double overload) +static double overload_start(void) { + return get_param_flt(global.parameters, "rules.ship.overload.start", 1.1); +} + +static double overload_worse(void) { + return get_param_flt(global.parameters, "rules.ship.overload.worse", 1.5); +} + +static double overload_worst(void) { + return get_param_flt(global.parameters, "rules.ship.overload.worst", 5.0); +} + +static double overload_default_damage(void) { + return get_param_flt(global.parameters, "rules.ship.overload.damage.default", 0.05); +} + +static double overload_max_damage(void) { + return get_param_flt(global.parameters, "rules.ship.overload.damage.max", 0.37); +} + +double damage_overload(double overload) { - static double damage; - damage = get_param_flt(global.parameters, "rules.ship.damage_overload_min", 0.2); - damage *= overload - OVERLOAD_THRESHOLD + 1; - damage = _min(damage, get_param_flt(global.parameters, "rules.ship.damage_overload_max", 0.37)); + double damage, badness; + if (overload < overload_start()) + return 0; + damage = overload_default_damage(); + badness = overload - overload_worse(); + if (badness >= 0) { + damage += _min(badness, overload_worst() - overload_worse()) * + (overload_max_damage() - damage) / + (overload_worst() - overload_worse()); + } return damage; } @@ -794,7 +820,7 @@ static void drifting_ships(region * r) } ovl = overload(r, sh); - if (ovl >= OVERLOAD_THRESHOLD) { + if (ovl >= overload_start()) { rnext = NULL; } else { /* Auswahl einer Richtung: Zuerst auf Land, dann @@ -830,7 +856,7 @@ static void drifting_ships(region * r) if (sh != NULL) { fset(sh, SF_DRIFTED); - if (ovl >= OVERLOAD_THRESHOLD) { + if (ovl >= overload_start()) { damage_ship(sh, damage_overload(ovl)); msg_to_ship_inmates(sh, &firstu, &lastu, msg_message("massive_overload", "ship", sh)); } else diff --git a/src/move.h b/src/move.h index 2f93bcd0a..c0b92d071 100644 --- a/src/move.h +++ b/src/move.h @@ -42,8 +42,6 @@ extern "C" { #define HORSESNEEDED 2 #define STRENGTHMULTIPLIER 50 /* multiplier for trollbelt */ -#define OVERLOAD_THRESHOLD 1.5 - /* ein mensch wiegt 10, traegt also 5, ein pferd wiegt 50, traegt also 20. ein ** wagen wird von zwei pferden gezogen und traegt total 140, davon 40 die ** pferde, macht nur noch 100, aber samt eigenem gewicht (40) macht also 140. */ diff --git a/src/move.test.c b/src/move.test.c index f1aefa34b..f09de43d6 100644 --- a/src/move.test.c +++ b/src/move.test.c @@ -314,7 +314,7 @@ void setup_drift (struct drift_fixture *fix) { test_create_world(); fix->st_boat = st_get_or_create("boat"); - fix->st_boat->cabins = 10000; + fix->st_boat->cabins = 20000; fix->u = test_create_unit(fix->f = test_create_faction(0), fix->r=findregion(-1,0)); assert(fix->r && fix->r->terrain->flags & SAIL_INTO); set_level(fix->u, SK_SAILING, fix->st_boat->sumskill); @@ -338,7 +338,7 @@ static void test_ship_normal_overload(CuTest *tc) { struct drift_fixture fix; setup_drift(&fix); - fix.u->number = 12; + fix.u->number = 21; movement(); CuAssertPtrEquals(tc, fix.u->region, findregion(0, 0)); CuAssertIntEquals(tc, 2, ship_damage_percent(fix.sh)); @@ -349,10 +349,10 @@ static void test_ship_big_overload(CuTest *tc) { struct drift_fixture fix; setup_drift(&fix); - fix.u->number = 15; + fix.u->number = 22; movement(); CuAssertPtrEquals(tc, fix.u->region, findregion(-1, 0)); - CuAssertIntEquals(tc, 20, ship_damage_percent(fix.sh)); + CuAssertIntEquals(tc, 5, ship_damage_percent(fix.sh)); CuAssertPtrNotNull(tc, test_find_messagetype(fix.f->msgs, "massive_overload")); } @@ -360,7 +360,7 @@ static void test_ship_no_real_overload(CuTest *tc) { struct drift_fixture fix; setup_drift(&fix); - fix.u->number = 12; + fix.u->number = 21; damage_ship(fix.sh, .80); movement(); CuAssertPtrEquals(tc, fix.u->region, findregion(0, 0)); @@ -372,7 +372,7 @@ static void test_ship_ridiculous_overload(CuTest *tc) { struct drift_fixture fix; setup_drift(&fix); - fix.u->number = 100; + fix.u->number = 500; movement(); CuAssertIntEquals(tc, 37, ship_damage_percent(fix.sh)); CuAssertPtrNotNull(tc, test_find_messagetype(fix.f->msgs, "massive_overload")); @@ -383,7 +383,7 @@ static void test_ship_ridiculous_overload_no_captain(CuTest *tc) { setup_drift(&fix); set_level(fix.u, SK_SAILING, 0); - fix.u->number = 100; + fix.u->number = 500; movement(); CuAssertIntEquals(tc, 37, ship_damage_percent(fix.sh)); CuAssertPtrNotNull(tc, test_find_messagetype(fix.f->msgs, "massive_overload")); @@ -393,8 +393,8 @@ static void test_ship_ridiculous_overload_bad(CuTest *tc) { struct drift_fixture fix; setup_drift(&fix); - fix.u->number = 200; - set_param(&global.parameters, "rules.ship.damage_overload_max", "1"); + fix.u->number = 500; + set_param(&global.parameters, "rules.ship.overload.damage.max", "1"); movement(); CuAssertTrue(tc, ship_damage_percent(fix.sh) > 99); CuAssertPtrNotNull(tc, test_find_messagetype(fix.f->msgs, "massive_overload")); @@ -402,6 +402,15 @@ static void test_ship_ridiculous_overload_bad(CuTest *tc) { CuAssertPtrNotNull(tc, test_find_messagetype(fix.f->msgs, "shipsink")); } +extern double damage_overload(double overload); + +static void test_ship_damage_overload(CuTest *tc) { + CuAssertDblEquals(tc, 0.0, damage_overload(1), ASSERT_DBL_DELTA); + CuAssertDblEquals(tc, 0.05, damage_overload(1.1), ASSERT_DBL_DELTA); + CuAssertDblEquals(tc, 0.05, damage_overload(1.5), ASSERT_DBL_DELTA); + CuAssertDblEquals(tc, 0.21, damage_overload(3.25), ASSERT_DBL_DELTA); + CuAssertDblEquals(tc, 0.37, damage_overload(5), ASSERT_DBL_DELTA); +} CuSuite *get_move_suite(void) { @@ -424,5 +433,6 @@ CuSuite *get_move_suite(void) SUITE_ADD_TEST(suite, test_ship_ridiculous_overload); SUITE_ADD_TEST(suite, test_ship_ridiculous_overload_bad); SUITE_ADD_TEST(suite, test_ship_ridiculous_overload_no_captain); + SUITE_ADD_TEST(suite, test_ship_damage_overload); return suite; } From afd44fc50a060b8b0c33d97f6d96d091acda5d0e Mon Sep 17 00:00:00 2001 From: Steffen Mecke Date: Thu, 19 Nov 2015 15:28:21 +0100 Subject: [PATCH 4/5] fix test interdependence; set silver weight to 1 --- src/move.test.c | 42 +++++++++++++++++++++++++++++++++++++----- src/tests.c | 1 + 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/move.test.c b/src/move.test.c index 176740a86..f550a20bf 100644 --- a/src/move.test.c +++ b/src/move.test.c @@ -317,9 +317,10 @@ void setup_drift (struct drift_fixture *fix) { fix->lang = get_or_create_locale("de"); test_create_world(); - - fix->st_boat = st_get_or_create("boat"); + test_create_shiptype("drifter"); + fix->st_boat = st_get_or_create("drifter"); fix->st_boat->cabins = 20000; + fix->u = test_create_unit(fix->f = test_create_faction(0), fix->r=findregion(-1,0)); assert(fix->r && fix->r->terrain->flags & SAIL_INTO); set_level(fix->u, SK_SAILING, fix->st_boat->sumskill); @@ -331,16 +332,24 @@ void setup_drift (struct drift_fixture *fix) { static void test_ship_no_overload(CuTest *tc) { struct drift_fixture fix; + + test_cleanup(); + setup_drift(&fix); fix.u->number = 2; movement(); CuAssertPtrEquals(tc, fix.u->region, findregion(-1,0)); CuAssertIntEquals(tc, 0, fix.sh->damage); + + test_cleanup(); } static void test_ship_normal_overload(CuTest *tc) { struct drift_fixture fix; + + test_cleanup(); + setup_drift(&fix); fix.u->number = 21; @@ -348,10 +357,15 @@ static void test_ship_normal_overload(CuTest *tc) { CuAssertPtrEquals(tc, fix.u->region, findregion(0, 0)); CuAssertIntEquals(tc, 2, ship_damage_percent(fix.sh)); CuAssertPtrNotNull(tc, test_find_messagetype(fix.f->msgs, "ship_drift")); + + test_cleanup(); } static void test_ship_big_overload(CuTest *tc) { struct drift_fixture fix; + + test_cleanup(); + setup_drift(&fix); fix.u->number = 22; @@ -359,10 +373,15 @@ static void test_ship_big_overload(CuTest *tc) { CuAssertPtrEquals(tc, fix.u->region, findregion(-1, 0)); CuAssertIntEquals(tc, 5, ship_damage_percent(fix.sh)); CuAssertPtrNotNull(tc, test_find_messagetype(fix.f->msgs, "massive_overload")); + + test_cleanup(); } static void test_ship_no_real_overload(CuTest *tc) { struct drift_fixture fix; + + test_cleanup(); + setup_drift(&fix); fix.u->number = 21; @@ -371,20 +390,30 @@ static void test_ship_no_real_overload(CuTest *tc) { CuAssertPtrEquals(tc, fix.u->region, findregion(0, 0)); CuAssertIntEquals(tc, 82, ship_damage_percent(fix.sh)); CuAssertPtrEquals(tc, 0, test_find_messagetype(fix.f->msgs, "massive_overload")); + + test_cleanup(); } static void test_ship_ridiculous_overload(CuTest *tc) { struct drift_fixture fix; + + test_cleanup(); + setup_drift(&fix); fix.u->number = 500; movement(); CuAssertIntEquals(tc, 37, ship_damage_percent(fix.sh)); CuAssertPtrNotNull(tc, test_find_messagetype(fix.f->msgs, "massive_overload")); + + test_cleanup(); } static void test_ship_ridiculous_overload_no_captain(CuTest *tc) { struct drift_fixture fix; + + test_cleanup(); + setup_drift(&fix); set_level(fix.u, SK_SAILING, 0); @@ -392,10 +421,14 @@ static void test_ship_ridiculous_overload_no_captain(CuTest *tc) { movement(); CuAssertIntEquals(tc, 37, ship_damage_percent(fix.sh)); CuAssertPtrNotNull(tc, test_find_messagetype(fix.f->msgs, "massive_overload")); + + test_cleanup(); } static void test_ship_ridiculous_overload_bad(CuTest *tc) { struct drift_fixture fix; + + test_cleanup(); setup_drift(&fix); fix.u->number = 500; @@ -405,6 +438,7 @@ static void test_ship_ridiculous_overload_bad(CuTest *tc) { CuAssertPtrNotNull(tc, test_find_messagetype(fix.f->msgs, "massive_overload")); CuAssertPtrEquals(tc, 0, fix.sh->region); CuAssertPtrNotNull(tc, test_find_messagetype(fix.f->msgs, "shipsink")); + test_cleanup(); } extern double damage_overload(double overload); @@ -431,7 +465,6 @@ static void test_follow_ship_msg(CuTest * tc) { const ship_type *stype; message *msg; order *ord; - item_type *silver; traveldir *td = NULL; attrib *a; @@ -452,8 +485,7 @@ static void test_follow_ship_msg(CuTest * tc) { assert(ord); unit_addorder(u, ord); - silver = get_resourcetype(R_SILVER)->itype; - i_change(&u->items, silver, 999999); + set_money(u, 999999); a = a_add(&(r->attribs), a_new(&at_shiptrail)); td = (traveldir *)a->data.v; diff --git a/src/tests.c b/src/tests.c index 264416458..a0fc89a17 100644 --- a/src/tests.c +++ b/src/tests.c @@ -235,6 +235,7 @@ void test_create_world(void) locale_setstring(loc, keyword(K_RESERVE), "RESERVIEREN"); locale_setstring(loc, "money", "SILBER"); init_resources(); + get_resourcetype(R_SILVER)->itype->weight = 1; test_create_horse(); From ae870ff53e49abbc5e1a7700d3ac3ee204301b48 Mon Sep 17 00:00:00 2001 From: Steffen Mecke Date: Thu, 19 Nov 2015 16:40:22 +0100 Subject: [PATCH 5/5] fix division by zero --- src/move.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/move.c b/src/move.c index 79ca964d6..96e4801cf 100644 --- a/src/move.c +++ b/src/move.c @@ -749,6 +749,7 @@ double damage_overload(double overload) damage = overload_default_damage(); badness = overload - overload_worse(); if (badness >= 0) { + assert(overload_worst() > overload_worse() || !"overload.worst must be > overload.worse"); damage += _min(badness, overload_worst() - overload_worse()) * (overload_max_damage() - damage) / (overload_worst() - overload_worse());