diff --git a/res/e3a/races.xml b/res/e3a/races.xml
index e2fea2acb..117759ca8 100644
--- a/res/e3a/races.xml
+++ b/res/e3a/races.xml
@@ -703,8 +703,8 @@
+ walk="yes" learn="no" teach="no" noheal="yes" desert="yes"
+ undead="yes" equipment="yes" giveperson="yes" unarmedguard="yes">
@@ -801,7 +801,7 @@
-
+
@@ -816,7 +816,7 @@
-
+
diff --git a/res/eressea/races.xml b/res/eressea/races.xml
index c858aceb1..82b642ba2 100644
--- a/res/eressea/races.xml
+++ b/res/eressea/races.xml
@@ -732,7 +732,7 @@
-
+
@@ -1044,7 +1044,7 @@
-
+
@@ -1058,7 +1058,7 @@
-
+
diff --git a/scripts/tests/e2/ships.lua b/scripts/tests/e2/ships.lua
index 416677d26..5b77220ec 100644
--- a/scripts/tests/e2/ships.lua
+++ b/scripts/tests/e2/ships.lua
@@ -312,7 +312,6 @@ function test_give_ship_compatible_coasts()
assert_equal(1, u1.ship.number)
assert_equal(4, u2.ship.number)
assert_equal(2, u2.ship.coast)
-
end
function test_give_ship_only_from_captain()
@@ -453,3 +452,66 @@ function test_no_speedsail_on_convoy()
process_orders()
assert_equal(nil, sh:get_curse('shipspeedup'))
end
+
+function test_build_ship()
+ local r = region.create(1, 0, 'plain')
+ local f = faction.create("insect")
+ local u = unit.create(f, r, 25)
+ local sh = ship.create(r, 'longboat')
+ u.ship = sh
+ sh.size = 25
+ u:set_skill('shipcraft', 1)
+ u:add_item("log", 50)
+ u:add_order("MACHE SCHIFF " .. itoa36(sh.id))
+ process_orders()
+ assert_equal(50, sh.size)
+ assert_equal(25, u:get_item('log'))
+end
+
+function test_build_convoy()
+ local r = region.create(1, 0, 'plain')
+ local f = faction.create("insect")
+ local u = unit.create(f, r, 50)
+ local sh = ship.create(r, 'longboat')
+ u.ship = sh
+ sh.number = 2
+ sh.size = 25
+ u:set_skill('shipcraft', 1)
+ u:add_item("log", 100)
+ u:add_order("MACHE SCHIFF " .. itoa36(sh.id))
+ process_orders()
+ assert_equal(75, sh.size)
+ assert_equal(50, u:get_item('log'))
+end
+
+function test_repair_convoy()
+ local r = region.create(1, 0, 'plain')
+ local f = faction.create("insect")
+ local u = unit.create(f, r, 50)
+ local sh = ship.create(r, 'longboat')
+ u.ship = sh
+ sh.number = 2
+ sh.damage = 7500 -- 75 Holz
+ u:set_skill('shipcraft', 1)
+ u:add_item("log", 100)
+ u:add_order("MACHE SCHIFF " .. itoa36(sh.id))
+ process_orders()
+ assert_equal(2500, sh.damage)
+ assert_equal(50, u:get_item('log'))
+end
+
+function test_build_convoy_max()
+ local r = region.create(1, 0, 'plain')
+ local f = faction.create("insect")
+ local u = unit.create(f, r, 100)
+ local sh = ship.create(r, 'longboat')
+ u.ship = sh
+ sh.number = 2
+ sh.size = 25
+ u:set_skill('shipcraft', 1)
+ u:add_item("log", 100)
+ u:add_order("MACHE SCHIFF " .. itoa36(sh.id))
+ process_orders()
+ assert_equal(100, sh.size)
+ assert_equal(25, u:get_item('log'))
+end
diff --git a/src/creport.c b/src/creport.c
index 46b998066..f9bffd177 100644
--- a/src/creport.c
+++ b/src/creport.c
@@ -700,8 +700,7 @@ static void cr_output_ship(struct stream *out, const ship *sh, const unit *u,
stream_printf(out, "%d;Anzahl\n", sh->number);
stream_printf(out, "%d;Groesse\n", sh->size);
if (sh->damage) {
- int percent =
- (sh->damage * 100 + DAMAGE_SCALE - 1) / (sh->size * DAMAGE_SCALE);
+ int percent = ship_damage_percent(sh);
stream_printf(out, "%d;Schaden\n", percent);
}
if (u) {
diff --git a/src/economy.c b/src/economy.c
index b73845c06..7cd28f6b9 100644
--- a/src/economy.c
+++ b/src/economy.c
@@ -509,7 +509,7 @@ static void manufacture(unit * u, const item_type * itype, int want)
if (want == 0) {
want = maxbuild(u, itype->construction);
}
- n = build(u, itype->construction, 0, want, skill_mod);
+ n = build(u, 1, itype->construction, 0, want, skill_mod);
switch (n) {
case ENEEDSKILL:
ADDMSG(&u->faction->msgs,
@@ -607,9 +607,8 @@ static void allocate_resource(unit * u, const resource_type * rtype, int want)
if (itype->rtype && (itype->rtype == get_resourcetype(R_IRON) || itype->rtype == rt_find("laen"))) {
unit *u2;
for (u2 = r->units; u2; u2 = u2->next) {
- if (is_guard(u)
- && !fval(u2, UFL_ISNEW)
- && u2->number && !alliedunit(u2, u->faction, HELP_GUARD)) {
+ if (!fval(u2, UFL_ISNEW) && u2->number
+ && is_guard(u2) && !alliedunit(u2, u->faction, HELP_GUARD)) {
ADDMSG(&u->faction->msgs,
msg_feedback(u, u->thisorder, "region_guarded", "guard", u2));
return;
@@ -851,7 +850,7 @@ static void create_potion(unit * u, const item_type * itype, int want)
if (want == 0) {
want = maxbuild(u, itype->construction);
}
- built = build(u, itype->construction, 0, want, 0);
+ built = build(u, 1, itype->construction, 0, want, 0);
switch (built) {
case ELOWSKILL:
case ENEEDSKILL:
diff --git a/src/kernel/build.c b/src/kernel/build.c
index 447db8662..d9e1cc8bc 100644
--- a/src/kernel/build.c
+++ b/src/kernel/build.c
@@ -224,7 +224,7 @@ int destroy_cmd(unit * u, struct order *ord)
return 14;
}
- if (n >= (sh->size * 100) / sh->type->construction->maxsize) {
+ if (n >= (sh->size * 100) / ship_maxsize(sh)) {
/* destroy completly */
/* all units leave the ship */
for (u2 = r->units; u2; u2 = u2->next) {
@@ -239,7 +239,7 @@ int destroy_cmd(unit * u, struct order *ord)
}
else {
/* partial destroy */
- sh->size -= (sh->type->construction->maxsize * n) / 100;
+ sh->size -= (ship_maxsize(sh) * n) / 100;
ADDMSG(&u->faction->msgs, msg_message("shipdestroy_partial",
"unit region ship", u, r, sh));
}
@@ -509,9 +509,9 @@ int build_skill(unit *u, int basesk, int skill_mod) {
* of the first object have already been finished. return the
* actual size that could be built.
*/
-static int build_limited(unit * u, const construction * con, int completed, int want, int basesk, int *skill_total) {
+static int build_limited(unit * u, const construction * con, int completed, int number, int want, int basesk, int *skill_total) {
int skills = *skill_total;
- int made = 0;
+ int made = 0, maxsize = con->maxsize * number;
if (want <= 0) {
return 0;
@@ -519,7 +519,7 @@ static int build_limited(unit * u, const construction * con, int completed, int
if (con == NULL) {
return ENOMATERIALS;
}
- if (completed == con->maxsize) {
+ if (completed == maxsize) {
return ECOMPLETE;
}
for (; want > 0 && skills > 0;) {
@@ -530,8 +530,8 @@ static int build_limited(unit * u, const construction * con, int completed, int
* (enno): Nein, das ist fuer Dinge, bei denen die naechste Ausbaustufe
* die gleiche wie die vorherige ist. z.b. Gegenstaende.
*/
- if (con->maxsize > 0) {
- completed = completed % con->maxsize;
+ if (maxsize > 0) {
+ completed = completed % (maxsize);
}
else {
completed = 0;
@@ -566,8 +566,8 @@ static int build_limited(unit * u, const construction * con, int completed, int
if (want < n) n = want;
- if (con->maxsize > 0) {
- int req = con->maxsize - completed;
+ if (maxsize > 0) {
+ int req = maxsize - completed;
if (req < n) n = req;
want = n;
}
@@ -592,11 +592,12 @@ static int build_limited(unit * u, const construction * con, int completed, int
return made;
}
-int build(unit * u, const construction * con, int completed, int want, int skill_mod)
+int build(unit * u, int number, const construction * con, int completed, int want, int skill_mod)
{
int skills = INT_MAX; /* number of skill points remainig */
int made, basesk = 0;
+ assert(number >= 1);
assert(con->skill != NOSKILL);
basesk = effskill(u, con->skill, NULL);
if (basesk == 0) {
@@ -604,7 +605,7 @@ int build(unit * u, const construction * con, int completed, int want, int skill
}
skills = build_skill(u, basesk, skill_mod);
- made = build_limited(u, con, completed, want, basesk, &skills);
+ made = build_limited(u, con, completed, number, want, basesk, &skills);
/* Nur soviel PRODUCEEXP wie auch tatsaechlich gemacht wurde */
if (made > 0) {
produceexp(u, con->skill, (made < u->number) ? made : u->number);
@@ -698,7 +699,7 @@ static int build_stages(unit *u, const building_type *btype, int built, int n, i
want = todo;
}
}
- err = build_limited(u, con, built, want, basesk, skill_total);
+ err = build_limited(u, con, 1, built, want, basesk, skill_total);
if (err < 0) {
if (made == 0) {
/* could not make any part at all */
@@ -904,9 +905,9 @@ static void build_ship(unit * u, ship * sh, int want)
const construction *construction = sh->type->construction;
int size = (sh->size * DAMAGE_SCALE - sh->damage) / DAMAGE_SCALE;
int n;
- int can = build(u, construction, size, want, 0);
+ int can = build(u, sh->number, construction, size, want, 0);
- if ((n = construction->maxsize - sh->size) > 0 && can > 0) {
+ if ((n = ship_maxsize(sh) - sh->size) > 0 && can > 0) {
if (can >= n) {
sh->size += n;
can -= n;
@@ -1000,11 +1001,12 @@ void continue_ship(unit * u, int want)
cmistake(u, u->thisorder, 20, MSG_PRODUCE);
return;
}
- cons = sh->type->construction;
- if (sh->size == cons->maxsize && !sh->damage) {
+ msize = ship_maxsize(sh);
+ if (sh->size >= msize && !sh->damage) {
cmistake(u, u->thisorder, 16, MSG_PRODUCE);
return;
}
+ cons = sh->type->construction;
if (effskill(u, cons->skill, NULL) < cons->minskill) {
ADDMSG(&u->faction->msgs, msg_feedback(u, u->thisorder,
"error_build_skill_low", "value", cons->minskill));
diff --git a/src/kernel/build.h b/src/kernel/build.h
index d37d7fa12..523ec0e63 100644
--- a/src/kernel/build.h
+++ b/src/kernel/build.h
@@ -44,7 +44,7 @@ extern "C" {
void sunhash(struct ship *sh);
int roqf_factor(void);
- int build(struct unit *u, const construction * ctype, int completed, int want, int skill_mod);
+ int build(struct unit *u, int number, const construction * ctype, int completed, int want, int skill_mod);
int maxbuild(const struct unit *u, const construction * cons);
struct message *msg_materials_required(struct unit *u, struct order *ord,
const struct construction *ctype, int multi);
diff --git a/src/kernel/build.test.c b/src/kernel/build.test.c
index 4eba0baaf..5534ebc32 100644
--- a/src/kernel/build.test.c
+++ b/src/kernel/build.test.c
@@ -145,10 +145,10 @@ static void test_build_requires_materials(CuTest *tc) {
u = setup_build(&bf);
set_level(u, SK_ARMORER, 2);
- CuAssertIntEquals(tc, ENOMATERIALS, build(u, &bf.cons, 0, 1, 0));
+ CuAssertIntEquals(tc, ENOMATERIALS, build(u, 1, &bf.cons, 0, 1, 0));
itype = bf.cons.materials[0].rtype->itype;
i_change(&u->items, itype, 2);
- CuAssertIntEquals(tc, 1, build(u, &bf.cons, 0, 1, 0));
+ CuAssertIntEquals(tc, 1, build(u, 1, &bf.cons, 0, 1, 0));
CuAssertIntEquals(tc, 1, i_get(u->items, itype));
teardown_build(&bf);
}
@@ -161,7 +161,7 @@ static void test_build_failure_missing_skill(CuTest *tc) {
u = setup_build(&bf);
rtype = bf.cons.materials[0].rtype;
i_change(&u->items, rtype->itype, 1);
- CuAssertIntEquals(tc, ENEEDSKILL, build(u, &bf.cons, 1, 1, 0));
+ CuAssertIntEquals(tc, ENEEDSKILL, build(u, 1, &bf.cons, 1, 1, 0));
teardown_build(&bf);
}
@@ -174,7 +174,7 @@ static void test_build_failure_low_skill(CuTest *tc) {
rtype = bf.cons.materials[0].rtype;
i_change(&u->items, rtype->itype, 1);
set_level(u, SK_ARMORER, bf.cons.minskill - 1);
- CuAssertIntEquals(tc, ELOWSKILL, build(u, &bf.cons, 0, 10, 0));
+ CuAssertIntEquals(tc, ELOWSKILL, build(u, 1, &bf.cons, 0, 10, 0));
teardown_build(&bf);
}
@@ -188,7 +188,7 @@ static void test_build_failure_completed(CuTest *tc) {
i_change(&u->items, rtype->itype, 1);
set_level(u, SK_ARMORER, bf.cons.minskill);
bf.cons.maxsize = 1;
- CuAssertIntEquals(tc, ECOMPLETE, build(u, &bf.cons, bf.cons.maxsize, 10, 0));
+ CuAssertIntEquals(tc, ECOMPLETE, build(u, 1, &bf.cons, bf.cons.maxsize, 10, 0));
CuAssertIntEquals(tc, 1, i_get(u->items, rtype->itype));
teardown_build(&bf);
}
@@ -203,19 +203,19 @@ static void test_build_limits(CuTest *tc) {
assert(rtype);
i_change(&u->items, rtype->itype, 1);
set_level(u, SK_ARMORER, bf.cons.minskill);
- CuAssertIntEquals(tc, 1, build(u, &bf.cons, 0, 10, 0));
+ CuAssertIntEquals(tc, 1, build(u, 1, &bf.cons, 0, 10, 0));
CuAssertIntEquals(tc, 0, i_get(u->items, rtype->itype));
scale_number(u, 2);
set_level(u, SK_ARMORER, bf.cons.minskill);
i_change(&u->items, rtype->itype, 2);
- CuAssertIntEquals(tc, 2, build(u, &bf.cons, 0, 10, 0));
+ CuAssertIntEquals(tc, 2, build(u, 1, &bf.cons, 0, 10, 0));
CuAssertIntEquals(tc, 0, i_get(u->items, rtype->itype));
scale_number(u, 2);
set_level(u, SK_ARMORER, bf.cons.minskill * 2);
i_change(&u->items, rtype->itype, 4);
- CuAssertIntEquals(tc, 4, build(u, &bf.cons, 0, 10, 0));
+ CuAssertIntEquals(tc, 4, build(u, 1, &bf.cons, 0, 10, 0));
CuAssertIntEquals(tc, 0, i_get(u->items, rtype->itype));
teardown_build(&bf);
}
@@ -234,7 +234,7 @@ static void test_build_with_ring(CuTest *tc) {
set_level(u, SK_ARMORER, bf.cons.minskill);
i_change(&u->items, rtype->itype, 20);
i_change(&u->items, ring, 1);
- CuAssertIntEquals(tc, 10, build(u, &bf.cons, 0, 20, 0));
+ CuAssertIntEquals(tc, 10, build(u, 1, &bf.cons, 0, 20, 0));
CuAssertIntEquals(tc, 10, i_get(u->items, rtype->itype));
teardown_build(&bf);
}
@@ -253,16 +253,16 @@ static void test_build_with_potion(CuTest *tc) {
i_change(&u->items, rtype->itype, 20);
change_effect(u, ptype, 4);
set_level(u, SK_ARMORER, bf.cons.minskill);
- CuAssertIntEquals(tc, 2, build(u, &bf.cons, 0, 20, 0));
+ CuAssertIntEquals(tc, 2, build(u, 1, &bf.cons, 0, 20, 0));
CuAssertIntEquals(tc, 18, i_get(u->items, rtype->itype));
CuAssertIntEquals(tc, 3, get_effect(u, ptype));
set_level(u, SK_ARMORER, bf.cons.minskill * 2);
- CuAssertIntEquals(tc, 4, build(u, &bf.cons, 0, 20, 0));
+ CuAssertIntEquals(tc, 4, build(u, 1, &bf.cons, 0, 20, 0));
CuAssertIntEquals(tc, 2, get_effect(u, ptype));
set_level(u, SK_ARMORER, bf.cons.minskill);
scale_number(u, 2); /* OBS: this scales the effects, too: */
CuAssertIntEquals(tc, 4, get_effect(u, ptype));
- CuAssertIntEquals(tc, 4, build(u, &bf.cons, 0, 20, 0));
+ CuAssertIntEquals(tc, 4, build(u, 1, &bf.cons, 0, 20, 0));
CuAssertIntEquals(tc, 2, get_effect(u, ptype));
teardown_build(&bf);
}
diff --git a/src/kernel/ship.c b/src/kernel/ship.c
index 601c36d82..56551e0f2 100644
--- a/src/kernel/ship.c
+++ b/src/kernel/ship.c
@@ -373,10 +373,15 @@ const char *shipname(const ship * sh)
return write_shipname(sh, ibuf, sizeof(idbuf[0]));
}
+int ship_maxsize(const ship *sh)
+{
+ return sh->number * sh->type->construction->maxsize;
+}
+
bool ship_finished(const ship *sh)
{
if (sh->type->construction) {
- return (sh->size >= sh->number * sh->type->construction->maxsize);
+ return (sh->size >= ship_maxsize(sh));
}
return true;
}
@@ -533,5 +538,8 @@ const char *ship_getname(const ship * sh)
}
int ship_damage_percent(const ship *sh) {
- return (sh->damage * 100 + DAMAGE_SCALE - 1) / (sh->size * DAMAGE_SCALE);
+ /* Schaden muss granularer sein als Größe, deshalb ist er skaliert
+ * DAMAGE_SCALE ist der Faktor zwischen 1 Schadenspunkt und 1 Größenpunkt.
+ */
+ return ((DAMAGE_SCALE - 1) + sh->damage * 100) / (sh->size * DAMAGE_SCALE);
}
diff --git a/src/kernel/ship.h b/src/kernel/ship.h
index a9a678848..d21f9f3c3 100644
--- a/src/kernel/ship.h
+++ b/src/kernel/ship.h
@@ -93,20 +93,20 @@ extern "C" {
const char *shipname(const struct ship *self);
int ship_capacity(const struct ship *sh);
int ship_cabins(const struct ship *sh);
+ int ship_maxsize(const struct ship *sh);
bool ship_finished(const struct ship *sh);
- extern void getshipweight(const struct ship *sh, int *weight, int *cabins);
+ void getshipweight(const struct ship *sh, int *weight, int *cabins);
- extern ship *new_ship(const struct ship_type *stype, struct region *r,
+ ship *new_ship(const struct ship_type *stype, struct region *r,
const struct locale *lang);
- extern const char *write_shipname(const struct ship *sh, char *buffer,
+ const char *write_shipname(const struct ship *sh, char *buffer,
size_t size);
- extern struct ship *findship(int n);
- extern struct ship *findshipr(const struct region *r, int n);
+ struct ship *findship(int n);
- extern const struct ship_type *findshiptype(const char *s,
+ const struct ship_type *findshiptype(const char *s,
const struct locale *lang);
- extern void write_ship_reference(const struct ship *sh,
+ void write_ship_reference(const struct ship *sh,
struct storage *store);
void remove_ship(struct ship **slist, struct ship *s);
diff --git a/src/modules/autoseed.c b/src/modules/autoseed.c
index 2cf931498..fca838c2a 100644
--- a/src/modules/autoseed.c
+++ b/src/modules/autoseed.c
@@ -874,10 +874,10 @@ int build_island(int x, int y, int minsize, newfaction ** players, int numfactio
fset(r, RF_MARK);
if (r->land) {
if (nsize < minsize) {
- nsize += random_neighbours(r, &rlist, &random_terrain, minsize - nsize);
+ nsize += random_neighbours(r, &rlist, random_terrain, minsize - nsize);
}
else {
- nsize += random_neighbours(r, &rlist, &get_ocean, minsize - nsize);
+ nsize += random_neighbours(r, &rlist, get_ocean, minsize - nsize);
}
}
regionqueue_push(&island, r);
diff --git a/src/move.c b/src/move.c
index 484ee993f..7a636d2f0 100644
--- a/src/move.c
+++ b/src/move.c
@@ -2227,6 +2227,7 @@ int follow_ship(unit * u, order * ord)
int moves, id, speed;
char command[256];
direction_t dir;
+ ship *sh;
if (fval(u, UFL_NOTMOVING)) {
return 0;
@@ -2251,10 +2252,10 @@ int follow_ship(unit * u, order * ord)
return 0;
}
+ sh = findship(id);
dir = hunted_dir(rc->attribs, id);
if (dir == NODIRECTION) {
- ship *sh = findship(id);
if (sh == NULL || sh->region != rc) {
cmistake(u, ord, 20, MSG_MOVE);
}
@@ -2278,7 +2279,7 @@ int follow_ship(unit * u, order * ord)
speed = maxspeed;
}
rc = rconnect(rc, dir);
- while (rc && moves < speed && (dir = hunted_dir(rc->attribs, id)) != NODIRECTION) {
+ while (rc && rc != sh->region && moves < speed && (dir = hunted_dir(rc->attribs, id)) != NODIRECTION) {
const char *loc = LOC(u->faction->locale, directions[dir]);
sbs_strcat(&sbcmd, " ");
sbs_strcat(&sbcmd, loc);
diff --git a/src/report.c b/src/report.c
index 407db00ff..7fdf8e690 100644
--- a/src/report.c
+++ b/src/report.c
@@ -1737,10 +1737,10 @@ nr_ship(struct stream *out, const region *r, const ship * sh, const faction * f,
sbs_printf(&sbs, "%s, %s", shipname(sh), LOC(f->locale, sh->type->_name));
}
- if (sh->size != sh->type->construction->maxsize) {
+ if (!ship_finished(sh)) {
sbs_printf(&sbs, ", %s (%d/%d)",
LOC(f->locale, "nr_undercons"), sh->size,
- sh->type->construction->maxsize);
+ ship_maxsize(sh));
}
if (sh->damage) {
int percent = ship_damage_percent(sh);