diff --git a/.gitignore b/.gitignore index 66d38f2a9..1c6507e5d 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,4 @@ Thumbs.db .gdb_history *.cfg *.cmd +tmp/ diff --git a/src/kernel/ship.c b/src/kernel/ship.c index a01326bdd..305a0143e 100644 --- a/src/kernel/ship.c +++ b/src/kernel/ship.c @@ -282,6 +282,20 @@ static int ShipSpeedBonus(const unit * u) return 0; } +int crew_skill(const ship *sh) { + int n = 0; + unit *u; + + n = 0; + + for (u = sh->region->units; u; u = u->next) { + if (u->ship == sh) { + n += eff_skill(u, SK_SAILING, sh->region) * u->number; + } + } + return n; +} + int shipspeed(const ship * sh, const unit * u) { double k = sh->type->range; @@ -290,14 +304,19 @@ int shipspeed(const ship * sh, const unit * u) attrib *a; struct curse *c; + assert(sh); + if (!u) u = ship_owner(sh); + if (!u) return 0; + assert(u->ship == sh); + assert(u == ship_owner(sh)); + assert(sh->type->construction); + assert(sh->type->construction->improvement == NULL); /* sonst ist construction::size nicht ship_type::maxsize */ + if (!init) { init = true; stormwind_ct = ct_find("stormwind"); nodrift_ct = ct_find("nodrift"); } - - assert(u->ship == sh); - assert(sh->type->construction->improvement == NULL); /* sonst ist construction::size nicht ship_type::maxsize */ if (sh->size != sh->type->construction->maxsize) return 0; @@ -442,18 +461,3 @@ const char *ship_getname(const ship * self) { return self->name; } - -unit *get_captain(const ship * sh) -{ - const region *r = sh->region; - unit *u; - - for (u = r->units; u; u = u->next) { - if (u->ship == sh && u->number - && eff_skill(u, SK_SAILING, r) >= sh->type->cptskill) - return u; - } - - return NULL; -} - diff --git a/src/kernel/ship.h b/src/kernel/ship.h index f123242df..c8fb5dfe7 100644 --- a/src/kernel/ship.h +++ b/src/kernel/ship.h @@ -124,7 +124,7 @@ extern "C" { const char *ship_getname(const struct ship *self); void ship_setname(struct ship *self, const char *name); int shipspeed(const struct ship *sh, const struct unit *u); - struct unit *get_captain(const struct ship *sh); + int crew_skill(const struct ship *sh); #ifdef __cplusplus } diff --git a/src/kernel/ship.test.c b/src/kernel/ship.test.c index 6d0c1b0cb..ce3b02ffb 100644 --- a/src/kernel/ship.test.c +++ b/src/kernel/ship.test.c @@ -10,6 +10,7 @@ #include #include #include +#include static void test_register_ship(CuTest * tc) { @@ -367,6 +368,69 @@ static void test_stype_defaults(CuTest *tc) { test_cleanup(); } +static void test_crew_skill(CuTest *tc) { + ship *sh; + region *r; + struct faction *f; + int i; + + test_cleanup(); + test_create_world(); + r = findregion(0, 0); + f = test_create_faction(0); + assert(r && f); + sh = test_create_ship(r, st_find("boat")); + for (i = 0; i != 4; ++i) { + unit * u = test_create_unit(f, r); + set_level(u, SK_SAILING, 5); + u->ship = sh; + } + CuAssertIntEquals(tc, 20, crew_skill(sh)); + test_cleanup(); +} + +static void test_shipspeed(CuTest *tc) { + ship *sh; + ship_type *stype; + region *r; + struct faction *f; + unit *cap, *crew; + + test_cleanup(); + test_create_world(); + r = test_create_region(0, 0, test_create_terrain("ocean", 0)); + f = test_create_faction(0); + assert(r && f); + stype = test_create_shiptype("longboat"); + stype->cptskill = 1; + stype->sumskill = 10; + stype->minskill = 1; + stype->range = 2; + sh = test_create_ship(r, stype); + + CuAssertIntEquals_Msg(tc, "ship without a captain cannot move", 0, shipspeed(sh, NULL)); + + cap = test_create_unit(f, r); + crew = test_create_unit(f, r); + cap->ship = sh; + crew->ship = sh; + CuAssertPtrEquals(tc, cap, ship_owner(sh)); + set_level(cap, SK_SAILING, stype->cptskill); + set_level(crew, SK_SAILING, stype->sumskill - stype->cptskill); + CuAssertIntEquals_Msg(tc, "ship with fully skilled crew can sail at max speed", 2, shipspeed(sh, cap)); + + set_level(cap, SK_SAILING, stype->cptskill + 5); + CuAssertIntEquals_Msg(tc, "higher captain skill should not affect top speed", 2, shipspeed(sh, cap)); + set_level(cap, SK_SAILING, stype->cptskill); + + CuAssertIntEquals(tc, 2, shipspeed(sh, cap)); + + set_level(crew, SK_SAILING, (stype->sumskill - stype->cptskill) * 11); + set_level(cap, SK_SAILING, stype->cptskill + 10); + CuAssertIntEquals_Msg(tc, "regular skills should not exceed sh.range", 2, shipspeed(sh, cap)); + +} + CuSuite *get_ship_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -381,5 +445,6 @@ CuSuite *get_ship_suite(void) SUITE_ADD_TEST(suite, test_shipowner_goes_to_other_after_leave); SUITE_ADD_TEST(suite, test_shipowner_goes_to_same_faction_after_leave); SUITE_ADD_TEST(suite, test_shipowner_goes_to_empty_unit_after_leave); + SUITE_ADD_TEST(suite, test_shipspeed); return suite; } diff --git a/src/laws.c b/src/laws.c index af1347bd1..b60e15ac9 100755 --- a/src/laws.c +++ b/src/laws.c @@ -2797,17 +2797,17 @@ void sinkships(struct region * r) ship *sh = *shp; if (!sh->type->construction || sh->size >= sh->type->construction->maxsize) { - if (fval(r->terrain, SEA_REGION) && (!enoughsailors(sh, r) - || get_captain(sh) == NULL)) { - /* Schiff nicht seetüchtig */ - float dmg = get_param_flt(global.parameters, - "rules.ship.damage.nocrewocean", - 0.30F); - damage_ship(sh, dmg); - } - if (ship_owner(sh) == NULL) { - float dmg = get_param_flt(global.parameters, "rules.ship.damage.nocrew", - 0.05F); + if (fval(r->terrain, SEA_REGION)) { + if (!enoughsailors(sh, crew_skill(sh))) { + // ship is at sea, but not enough people to control it + float dmg = get_param_flt(global.parameters, + "rules.ship.damage.nocrewocean", + 0.30F); + damage_ship(sh, dmg); + } + } else if (!ship_owner(sh)) { + // any ship lying around without an owner slowly rots + float dmg = get_param_flt(global.parameters, "rules.ship.damage.nocrew", 0.05F); damage_ship(sh, dmg); } } diff --git a/src/move.c b/src/move.c index a9ed36b3e..7d6f69371 100644 --- a/src/move.c +++ b/src/move.c @@ -469,18 +469,9 @@ static bool cansail(const region * r, ship * sh) return true; } -int enoughsailors(const ship * sh, const region * r) +int enoughsailors(const ship * sh, int sumskill) { - int n; - unit *u; - - n = 0; - - for (u = r->units; u; u = u->next) { - if (u->ship == sh) - n += eff_skill(u, SK_SAILING, r) * u->number; - } - return n >= sh->type->sumskill; + return sumskill >= sh->type->sumskill; } /* ------------------------------------------------------------- */ @@ -778,7 +769,7 @@ static void drifting_ships(region * r) assert(sh->type->construction->improvement == NULL); /* sonst ist construction::size nicht ship_type::maxsize */ if (captain && sh->size == sh->type->construction->maxsize - && enoughsailors(sh, r) && cansail(r, sh)) { + && enoughsailors(sh, crew_skill(sh)) && cansail(r, sh)) { shp = &sh->next; continue; } @@ -1767,7 +1758,7 @@ static bool ship_ready(const region * r, unit * u) cmistake(u, u->thisorder, 15, MSG_MOVE); return false; } - if (!enoughsailors(u->ship, r)) { + 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); */ diff --git a/src/move.h b/src/move.h index f35012b07..7674c43d7 100644 --- a/src/move.h +++ b/src/move.h @@ -61,7 +61,7 @@ extern "C" { void run_to(struct unit *u, struct region *to); struct unit *is_guarded(struct region *r, struct unit *u, unsigned int mask); bool is_guard(const struct unit *u, unsigned int mask); - int enoughsailors(const struct ship *sh, const struct region *r); + int enoughsailors(const struct ship *sh, int sumskill); bool canswim(struct unit *u); bool canfly(struct unit *u); void travelthru(const struct unit *u, struct region *r); diff --git a/src/spy.c b/src/spy.c index a440ab3aa..d993a9ab7 100644 --- a/src/spy.c +++ b/src/spy.c @@ -342,7 +342,7 @@ int setstealth_cmd(unit * u, struct order *ord) return 0; } -static int crew_skill(region * r, faction * f, ship * sh, skill_t sk) +static int top_skill(region * r, faction * f, ship * sh, skill_t sk) { int value = 0; unit *u; @@ -517,7 +517,7 @@ int sabotage_cmd(unit * u, struct order *ord) r = u->region; if (u2->faction != u->faction) { skdiff = - eff_skill(u, SK_SPY, r) - crew_skill(r, u2->faction, sh, SK_PERCEPTION); + eff_skill(u, SK_SPY, r) - top_skill(r, u2->faction, sh, SK_PERCEPTION); } if (try_destruction(u, u2, sh, skdiff)) { sink_ship(r, sh, u);