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;