diff --git a/res/core/de/strings.xml b/res/core/de/strings.xml
index 75f57a6b0..c9a2ae616 100644
--- a/res/core/de/strings.xml
+++ b/res/core/de/strings.xml
@@ -7574,4 +7574,22 @@
+
+ Groaamm...
+
+
+ Tschrrrk...
+ Tshrrrk...
+
+
+ Schhhhh...
+ Shhhhhh...
+
+
+ Roaarrr...
+
+
+ Chrrr...
+
+
diff --git a/res/core/messages.xml b/res/core/messages.xml
index 15de4a153..bb57fd32d 100644
--- a/res/core/messages.xml
+++ b/res/core/messages.xml
@@ -7293,8 +7293,8 @@
- "$unit($unit) konnte durch einen Heiltrank überleben."
- "$unit($unit) was saved by a healing potion."
+ "Eine Person von $unit($unit) konnte durch einen Heiltrank überleben."
+ "A fighter of $unit($unit) was saved by a healing potion."
@@ -8441,4 +8441,15 @@
"$unit($unit) in $region($region): '$order($command)' - Heroes cannot recruit."
+
+
+
+
+
+
+
+ "$unit($dragon): \"$localize($growl) $if($eq($number,1), "Ich rieche", "Wir riechen") etwas in $region($target)\"."
+ "$unit($dragon): \"$localize($growl) $if($eq($number,1), "I smell", "We smell") something in $region($target)\"."
+
+
diff --git a/res/core/ships.xml b/res/core/ships.xml
index 6c6821be4..0ce254328 100644
--- a/res/core/ships.xml
+++ b/res/core/ships.xml
@@ -1,7 +1,6 @@
-
@@ -9,7 +8,6 @@
-
@@ -17,7 +15,6 @@
-
@@ -25,7 +22,6 @@
-
@@ -33,7 +29,6 @@
-
@@ -48,7 +43,6 @@
-
@@ -65,7 +59,6 @@
-
diff --git a/res/e3a/ships.xml b/res/e3a/ships.xml
index 1174eda8e..b8c5f0197 100644
--- a/res/e3a/ships.xml
+++ b/res/e3a/ships.xml
@@ -1,7 +1,6 @@
-
@@ -17,7 +16,6 @@
-
@@ -28,7 +26,6 @@
-
@@ -44,7 +41,6 @@
-
@@ -61,7 +57,6 @@
-
@@ -78,7 +73,6 @@
-
@@ -89,7 +83,6 @@
-
@@ -98,7 +91,6 @@
-
@@ -109,7 +101,6 @@
-
@@ -120,7 +111,6 @@
-
@@ -133,7 +123,6 @@
-
@@ -145,7 +134,6 @@
-
diff --git a/res/ships/boat.xml b/res/ships/boat.xml
index 28d48392f..91852de92 100644
--- a/res/ships/boat.xml
+++ b/res/ships/boat.xml
@@ -1,6 +1,5 @@
-
diff --git a/src/alchemy.c b/src/alchemy.c
index 5419be4ee..5e369a3e1 100644
--- a/src/alchemy.c
+++ b/src/alchemy.c
@@ -318,6 +318,10 @@ static int a_readeffect(attrib * a, void *owner, struct gamedata *data)
if (rtype == NULL || rtype->ptype == NULL || power <= 0) {
return AT_READ_FAIL;
}
+ if (rtype->ptype==oldpotiontype[P_HEAL]) {
+ // healing potions used to have long-term effects
+ return AT_READ_FAIL;
+ }
edata->type = rtype->ptype;
edata->value = power;
return AT_READ_OK;
diff --git a/src/battle.c b/src/battle.c
index 231d7f2e1..217f73c34 100644
--- a/src/battle.c
+++ b/src/battle.c
@@ -690,7 +690,7 @@ static int CavalryBonus(const unit * u, troop enemy, int type)
int skl = effskill(u, SK_RIDING, 0);
/* only half against trolls */
if (skl > 0) {
- if (type == BONUS_DAMAGE) {
+ if (type == BONUS_SKILL) {
int dmg = _min(skl, 8);
if (u_race(enemy.fighter->unit) == get_race(RC_TROLL)) {
dmg = dmg / 4;
@@ -1156,7 +1156,6 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile)
unit *au = af->unit;
unit *du = df->unit;
battle *b = df->side->battle;
- int heiltrank = 0;
/* Schild */
side *ds = df->side;
@@ -1289,7 +1288,7 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile)
}
}
- assert(dt.index < du->number);
+ assert(dt.index >= 0 && dt.index < du->number);
if (rda>0) {
df->person[dt.index].hp -= rda;
if (u_race(au) == get_race(RC_DAEMON)) {
@@ -1314,36 +1313,23 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile)
df->person[dt.index].defence--;
}
}
- df->person[dt.index].flags = (df->person[dt.index].flags & ~FL_SLEEPING);
return false;
}
/* Sieben Leben */
if (u_race(du) == get_race(RC_CAT) && (chance(1.0 / 7))) {
- assert(dt.index >= 0 && dt.index < du->number);
df->person[dt.index].hp = unit_max_hp(du);
return false;
}
- /* Heiltrank schluerfen und hoffen */
- if (oldpotiontype[P_HEAL]) {
- if (get_effect(du, oldpotiontype[P_HEAL]) > 0) {
- change_effect(du, oldpotiontype[P_HEAL], -1);
- heiltrank = 1;
- }
- else if (i_get(du->items, oldpotiontype[P_HEAL]->itype) > 0) {
+ if (oldpotiontype[P_HEAL] && !fval(&df->person[dt.index], FL_HEALING_USED)) {
+ if (i_get(du->items, oldpotiontype[P_HEAL]->itype) > 0) {
i_change(&du->items, oldpotiontype[P_HEAL]->itype, -1);
- change_effect(du, oldpotiontype[P_HEAL], 3);
- heiltrank = 1;
- }
- if (heiltrank && (chance(0.50))) {
- {
- message *m = msg_message("battle::potionsave", "unit", du);
- message_faction(b, du->faction, m);
- msg_release(m);
- }
- assert(dt.index >= 0 && dt.index < du->number);
- df->person[dt.index].hp = u_race(du)->hitpoints;
+ message *m = msg_message("battle::potionsave", "unit", du);
+ message_faction(b, du->faction, m);
+ msg_release(m);
+ fset(&df->person[dt.index], FL_HEALING_USED);
+ df->person[dt.index].hp = u_race(du)->hitpoints * 5; /* give the person a buffer */
return false;
}
}
@@ -2384,7 +2370,7 @@ static void add_tactics(tactics * ta, fighter * fig, int value)
ta->value = value;
}
-static double horsebonus(const unit * u)
+static double horse_fleeing_bonus(const unit * u)
{
const item_type *it_horse, *it_elvenhorse, *it_charger;
int n1 = 0, n2 = 0, n3 = 0;
@@ -2392,8 +2378,6 @@ static double horsebonus(const unit * u)
int skl = effskill(u, SK_RIDING, 0);
const resource_type *rtype;
- if (skl < 1) return 0.0;
-
it_horse = ((rtype = get_resourcetype(R_HORSE)) != NULL) ? rtype->itype : 0;
it_elvenhorse = ((rtype = get_resourcetype(R_UNICORN)) != NULL) ? rtype->itype : 0;
it_charger = ((rtype = get_resourcetype(R_CHARGER)) != NULL) ? rtype->itype : 0;
@@ -2410,9 +2394,9 @@ static double horsebonus(const unit * u)
}
if (skl >= 5 && n3 >= u->number)
return 0.30;
- if (skl >= 3 && n2 + n3 >= u->number)
+ if (skl >= 2 && n2 + n3 >= u->number)
return 0.20;
- if (skl >= 1 && n1 + n2 + n3 >= u->number)
+ if (n1 + n2 + n3 >= u->number)
return 0.10;
return 0.0F;
}
@@ -2424,7 +2408,7 @@ double fleechance(unit * u)
/* Einheit u versucht, dem Getümmel zu entkommen */
c += (effskill(u, SK_STEALTH, 0) * 0.05);
- c += horsebonus(u);
+ c += horse_fleeing_bonus(u);
if (u_race(u) == get_race(RC_HALFLING)) {
c += 0.20;
diff --git a/src/battle.h b/src/battle.h
index 4ea3834d9..3e156070d 100644
--- a/src/battle.h
+++ b/src/battle.h
@@ -137,6 +137,7 @@ extern "C" {
#define FL_SLEEPING 16
#define FL_STUNNED 32 /* eine Runde keinen Angriff */
#define FL_HIT 64 /* the person at attacked */
+#define FL_HEALING_USED 128 /* has used a healing potion */
typedef struct troop {
struct fighter *fighter;
diff --git a/src/kernel/jsonconf.test.c b/src/kernel/jsonconf.test.c
index 63c5fe0ff..858c811b9 100644
--- a/src/kernel/jsonconf.test.c
+++ b/src/kernel/jsonconf.test.c
@@ -458,7 +458,7 @@ static void test_terrains(CuTest * tc)
"\"size\": 4000, "
"\"road\": 50, "
"\"seed\": 3, "
- "\"flags\" : [ \"forbidden\", \"arctic\", \"cavalry\", \"sea\", \"forest\", \"land\", \"sail\", \"fly\", \"swim\", \"walk\" ] } }}";
+ "\"flags\" : [ \"forbidden\", \"arctic\", \"cavalry\", \"sea\", \"forest\", \"land\", \"fly\", \"swim\", \"walk\" ] } }}";
const terrain_type *ter;
cJSON *json = cJSON_Parse(data);
@@ -470,7 +470,7 @@ static void test_terrains(CuTest * tc)
json_config(json);
ter = get_terrain("plain");
CuAssertPtrNotNull(tc, ter);
- CuAssertIntEquals(tc, ARCTIC_REGION | LAND_REGION | SEA_REGION | FOREST_REGION | CAVALRY_REGION | FORBIDDEN_REGION | FLY_INTO | WALK_INTO | SWIM_INTO | SAIL_INTO, ter->flags);
+ CuAssertIntEquals(tc, ARCTIC_REGION | LAND_REGION | SEA_REGION | FOREST_REGION | CAVALRY_REGION | FORBIDDEN_REGION | FLY_INTO | WALK_INTO | SWIM_INTO , ter->flags);
CuAssertIntEquals(tc, 4000, ter->size);
CuAssertIntEquals(tc, 50, ter->max_road);
CuAssertIntEquals(tc, 3, ter->distribution);
diff --git a/src/kernel/terrain.h b/src/kernel/terrain.h
index 931a74861..3d1491867 100644
--- a/src/kernel/terrain.h
+++ b/src/kernel/terrain.h
@@ -31,7 +31,6 @@ extern "C" {
#define CAVALRY_REGION (1<<4) /* riding in combat is possible */
/* Achtung: SEA_REGION ist nicht das Gegenteil von LAND_REGION. Die zwei schliessen sich nichtmal aus! */
#define FORBIDDEN_REGION (1<<5) /* unpassierbare Blockade-struct region */
-#define SAIL_INTO (1<<6) /* man darf hierhin segeln */
#define FLY_INTO (1<<7) /* man darf hierhin fliegen */
#define SWIM_INTO (1<<8) /* man darf hierhin schwimmen */
#define WALK_INTO (1<<9) /* man darf hierhin laufen */
diff --git a/src/laws.c b/src/laws.c
index 79889e7da..203970dc4 100755
--- a/src/laws.c
+++ b/src/laws.c
@@ -2213,26 +2213,13 @@ int send_cmd(unit * u, struct order *ord)
return 0;
}
-static bool display_item(faction * f, unit * u, const item_type * itype)
+static void display_item(unit * u, const item_type * itype)
{
+ faction * f = u->faction;
const char *name;
const char *key;
const char *info;
- if (u != NULL) {
- int i = i_get(u->items, itype);
- if (i == 0) {
- if (u->region->land != NULL) {
- i = i_get(u->region->land->items, itype);
- }
- if (i == 0) {
- i = i_get(u->faction->items, itype);
- if (i == 0)
- return false;
- }
- }
- }
-
name = resourcename(itype->rtype, 0);
key = mkname("iteminfo", name);
info = locale_getstring(f->locale, key);
@@ -2242,23 +2229,13 @@ static bool display_item(faction * f, unit * u, const item_type * itype)
}
ADDMSG(&f->msgs, msg_message("displayitem", "weight item description",
itype->weight, itype->rtype, info));
-
- return true;
}
-static bool display_potion(faction * f, unit * u, const potion_type * ptype)
+static void display_potion(unit * u, const potion_type * ptype)
{
+ faction * f = u->faction;
attrib *a;
- if (ptype == NULL)
- return false;
- else {
- int i = i_get(u->items, ptype->itype);
- if (i == 0 && 2 * ptype->level > effskill(u, SK_ALCHEMY, 0)) {
- return false;
- }
- }
-
a = a_find(f->attribs, &at_showitem);
while (a && a->data.v != ptype)
a = a->next;
@@ -2266,12 +2243,11 @@ static bool display_potion(faction * f, unit * u, const potion_type * ptype)
a = a_add(&f->attribs, a_new(&at_showitem));
a->data.v = (void *)ptype->itype;
}
-
- return true;
}
-static bool display_race(faction * f, unit * u, const race * rc)
+static void display_race(unit * u, const race * rc)
{
+ faction * f = u->faction;
const char *name, *key;
const char *info;
int a, at_count;
@@ -2279,8 +2255,6 @@ static bool display_race(faction * f, unit * u, const race * rc)
size_t size = sizeof(buf) - 1;
size_t bytes;
- if (u && u_race(u) != rc)
- return false;
name = rc_name_s(rc, NAME_SINGULAR);
bytes = slprintf(bufp, size, "%s: ", LOC(f->locale, name));
@@ -2294,7 +2268,7 @@ static bool display_race(faction * f, unit * u, const race * rc)
info = LOC(f->locale, mkname("raceinfo", "no_info"));
}
- bufp = STRLCPY(bufp, info, size);
+ if (info) bufp = STRLCPY(bufp, info, size);
/* hp_p : Trefferpunkte */
bytes =
@@ -2412,17 +2386,72 @@ static bool display_race(faction * f, unit * u, const race * rc)
*bufp = 0;
addmessage(0, f, buf, MSG_EVENT, ML_IMPORTANT);
+}
- return true;
+static void reshow_other(unit * u, struct order *ord, const char *s) {
+ int err = 21;
+
+ if (s) {
+ const spell *sp = 0;
+ const item_type *itype;
+ const race *rc;
+ /* check if it's an item */
+ itype = finditemtype(s, u->faction->locale);
+ sp = unit_getspell(u, s, u->faction->locale);
+ rc = findrace(s, u->faction->locale);
+
+ if (itype) {
+ // if this is a potion, we need the right alchemy skill
+ int i = i_get(u->items, itype);
+
+ err = 36; // we do not have this item?
+ if (i <= 0) {
+ // we don't have the item, but it may be a potion that we know
+ const potion_type *ptype = resource2potion(item2resource(itype));
+ if (ptype) {
+ if (2 * ptype->level > effskill(u, SK_ALCHEMY, 0)) {
+ itype = NULL;
+ }
+ } else {
+ itype = NULL;
+ }
+ }
+ }
+
+ if (itype) {
+ const potion_type *ptype = itype->rtype->ptype;
+ if (ptype) {
+ display_potion(u, ptype);
+ }
+ else {
+ display_item(u, itype);
+ }
+ return;
+ }
+
+ if (sp) {
+ attrib *a = a_find(u->faction->attribs, &at_seenspell);
+ while (a != NULL && a->type == &at_seenspell && a->data.v != sp) {
+ a = a->next;
+ }
+ if (a != NULL) {
+ a_remove(&u->faction->attribs, a);
+ }
+ return;
+ }
+
+ if (rc && u_race(u) == rc) {
+ display_race(u, rc);
+ return;
+ }
+ }
+ cmistake(u, ord, err, MSG_EVENT);
}
static void reshow(unit * u, struct order *ord, const char *s, param_t p)
{
int skill, c;
const potion_type *ptype;
- const item_type *itype;
- const spell *sp = 0;
- const race *rc;
switch (p) {
case P_ZAUBER:
@@ -2433,48 +2462,15 @@ static void reshow(unit * u, struct order *ord, const char *s, param_t p)
c = 0;
for (ptype = potiontypes; ptype != NULL; ptype = ptype->next) {
if (ptype->level * 2 <= skill) {
- c += display_potion(u->faction, u, ptype);
+ display_potion(u, ptype);
+ ++c;
}
}
if (c == 0)
cmistake(u, ord, 285, MSG_EVENT);
break;
case NOPARAM:
- if (s) {
- /* check if it's an item */
- itype = finditemtype(s, u->faction->locale);
- if (itype != NULL) {
- ptype = resource2potion(item2resource(itype));
- if (ptype != NULL) {
- if (display_potion(u->faction, u, ptype))
- break;
- }
- else {
- if (!display_item(u->faction, u, itype))
- cmistake(u, ord, 36, MSG_EVENT);
-
- break;
- }
- }
- /* try for a spell */
- sp = unit_getspell(u, s, u->faction->locale);
- if (sp) {
- attrib *a = a_find(u->faction->attribs, &at_seenspell);
- while (a != NULL && a->type == &at_seenspell && a->data.v != sp) {
- a = a->next;
- }
- if (a != NULL) {
- a_remove(&u->faction->attribs, a);
- }
- break;
- }
- /* last, check if it's a race. */
- rc = findrace(s, u->faction->locale);
- if (rc != NULL && display_race(u->faction, u, rc)) {
- break;
- }
- }
- cmistake(u, ord, 21, MSG_EVENT);
+ reshow_other(u, ord, s);
break;
default:
cmistake(u, ord, 222, MSG_EVENT);
diff --git a/src/laws.test.c b/src/laws.test.c
index 1b2136db7..623717619 100644
--- a/src/laws.test.c
+++ b/src/laws.test.c
@@ -27,6 +27,7 @@
#include
#include
+#include
static void test_new_building_can_be_renamed(CuTest * tc)
{
@@ -1251,6 +1252,83 @@ static void test_show_without_item(CuTest *tc)
test_cleanup();
}
+static void test_show_elf(CuTest *tc) {
+ order *ord;
+ race * rc;
+ unit *u;
+ struct locale *loc;
+ message * msg;
+
+ test_cleanup();
+
+ mt_register(mt_new_va("msg_event", "string:string", 0));
+ rc = test_create_race("elf");
+ test_create_itemtype("elvenhorse");
+
+ loc = get_or_create_locale("de");
+ locale_setstring(loc, "elvenhorse", "Elfenpferd");
+ locale_setstring(loc, "elvenhorse_p", "Elfenpferde");
+ locale_setstring(loc, "race::elf_p", "Elfen");
+ locale_setstring(loc, "race::elf", "Elf");
+ init_locale(loc);
+
+ CuAssertPtrNotNull(tc, finditemtype("elf", loc));
+ CuAssertPtrNotNull(tc, findrace("elf", loc));
+
+ u = test_create_unit(test_create_faction(rc), test_create_region(0, 0, 0));
+ u->faction->locale = loc;
+ ord = create_order(K_RESHOW, loc, "Elf");
+ reshow_cmd(u, ord);
+ CuAssertTrue(tc, test_find_messagetype(u->faction->msgs, "error36") == NULL);
+ msg = test_find_messagetype(u->faction->msgs, "msg_event");
+ CuAssertPtrNotNull(tc, msg);
+ CuAssertTrue(tc, memcmp("Elf:", msg->parameters[0].v, 4) == 0);
+ test_clear_messages(u->faction);
+ free_order(ord);
+ test_cleanup();
+}
+
+static void test_show_race(CuTest *tc) {
+ order *ord;
+ race * rc;
+ unit *u;
+ struct locale *loc;
+ message * msg;
+
+ test_cleanup();
+
+ mt_register(mt_new_va("msg_event", "string:string", 0));
+ test_create_race("human");
+ rc = test_create_race("elf");
+
+ loc = get_or_create_locale("de");
+ locale_setstring(loc, "race::elf_p", "Elfen");
+ locale_setstring(loc, "race::elf", "Elf");
+ locale_setstring(loc, "race::human_p", "Menschen");
+ locale_setstring(loc, "race::human", "Mensch");
+ init_locale(loc);
+ u = test_create_unit(test_create_faction(rc), test_create_region(0, 0, 0));
+ u->faction->locale = loc;
+
+ ord = create_order(K_RESHOW, loc, "Mensch");
+ reshow_cmd(u, ord);
+ CuAssertTrue(tc, test_find_messagetype(u->faction->msgs, "error21") != NULL);
+ CuAssertTrue(tc, test_find_messagetype(u->faction->msgs, "msg_event") == NULL);
+ test_clear_messages(u->faction);
+ free_order(ord);
+
+ ord = create_order(K_RESHOW, loc, "Elf");
+ reshow_cmd(u, ord);
+ CuAssertTrue(tc, test_find_messagetype(u->faction->msgs, "error21") == NULL);
+ msg = test_find_messagetype(u->faction->msgs, "msg_event");
+ CuAssertPtrNotNull(tc, msg);
+ CuAssertTrue(tc, memcmp("Elf:", msg->parameters[0].v, 4) == 0);
+ test_clear_messages(u->faction);
+ free_order(ord);
+
+ test_cleanup();
+}
+
static int low_wage(const region * r, const faction * f, const race * rc, int in_turn) {
return 1;
}
@@ -1386,6 +1464,8 @@ CuSuite *get_laws_suite(void)
SUITE_ADD_TEST(suite, test_name_building);
SUITE_ADD_TEST(suite, test_name_ship);
SUITE_ADD_TEST(suite, test_show_without_item);
+ SUITE_ADD_TEST(suite, test_show_elf);
+ SUITE_ADD_TEST(suite, test_show_race);
SUITE_ADD_TEST(suite, test_immigration);
SUITE_ADD_TEST(suite, test_demon_hunger);
diff --git a/src/modules/score.c b/src/modules/score.c
index 74eb0898b..2fd3cf230 100644
--- a/src/modules/score.c
+++ b/src/modules/score.c
@@ -167,7 +167,7 @@ void score(void)
fprintf(scoreFP, "(%s) ", score);
fprintf(scoreFP, "%30.30s (%3.3s) %5s (%3d)\n",
f->name,
- rc_name_s(f->race, NAME_SINGULAR),
+ f->race->_name,
factionid(f),
f->age);
}
diff --git a/src/monsters.c b/src/monsters.c
index 674ab0999..2fc2fcde3 100644
--- a/src/monsters.c
+++ b/src/monsters.c
@@ -547,21 +547,23 @@ static order *monster_seeks_target(region * r, unit * u)
}
#endif
-static const char *random_growl(void)
+void random_growl(const unit *u, region *target, int rand)
{
- switch (rng_int() % 5) {
- case 0:
- return "Groammm";
- case 1:
- return "Roaaarrrr";
- case 2:
- return "Chhhhhhhhhh";
- case 3:
- return "Tschrrrkk";
- case 4:
- return "Schhhh";
+ const struct locale *lang = u->faction->locale;
+ const char *growl;
+ switch(rand){
+ case 1: growl = "growl1"; break;
+ case 2: growl = "growl2"; break;
+ case 3: growl = "growl3"; break;
+ case 4: growl = "growl4"; break;
+ default: growl = "growl0";
+ }
+
+
+ if (rname(target, lang)) {
+ message *msg = msg_message("dragon_growl", "dragon number target growl", u, u->number, target, growl);
+ ADDMSG(&u->region->msgs, msg);
}
- return "";
}
extern struct attrib_type at_direction;
@@ -707,17 +709,7 @@ static order *plan_dragon(unit * u)
reduce_weight(u);
}
if (rng_int() % 100 < 15) {
- const struct locale *lang = u->faction->locale;
- /* do a growl */
- if (rname(tr, lang)) {
- addlist(&u->orders,
- create_order(K_MAIL, lang, "%s '%s... %s %s %s'",
- LOC(lang, parameters[P_REGION]),
- random_growl(),
- u->number ==
- 1 ? "Ich rieche" : "Wir riechen",
- "etwas in", rname(tr, u->faction->locale)));
- }
+ random_growl(u, tr, rng_int() % 5);
}
}
else {
diff --git a/src/monsters.test.c b/src/monsters.test.c
index a7a4d9b18..dd69e6bdb 100644
--- a/src/monsters.test.c
+++ b/src/monsters.test.c
@@ -12,10 +12,13 @@
#include "monster.h"
#include "guard.h"
+#include "reports.h"
#include "skill.h"
#include "study.h"
#include
+#include
+#include
#include
#include
@@ -55,7 +58,7 @@ static void create_monsters(faction **player, faction **monsters, unit **u, unit
fset(*monsters, FFL_NOIDLEOUT);
assert(fval(*monsters, FFL_NPC) && fval((*monsters)->race, RCF_UNARMEDGUARD) && fval((*monsters)->race, RCF_NPC) && fval(*monsters, FFL_NOIDLEOUT));
- test_create_region(-1, 0, test_create_terrain("ocean", SEA_REGION | SAIL_INTO | SWIM_INTO | FLY_INTO));
+ test_create_region(-1, 0, test_create_terrain("ocean", SEA_REGION | SWIM_INTO | FLY_INTO));
test_create_region(1, 0, 0);
r = test_create_region(0, 0, 0);
@@ -185,11 +188,14 @@ static void test_dragon_attacks_the_rich(CuTest * tc)
test_cleanup();
}
+extern void random_growl(const unit *u, region *tr, int rand);
+
static void test_dragon_moves(CuTest * tc)
{
faction *f, *f2;
region *r;
unit *u, *m;
+ struct message *msg;
create_monsters(&f, &f2, &u, &m);
rsetmoney(findregion(1, 0), 1000);
@@ -202,6 +208,18 @@ static void test_dragon_moves(CuTest * tc)
plan_monsters(f2);
CuAssertPtrNotNull(tc, find_order("move east", m));
+
+ mt_register(mt_new_va("dragon_growl", "dragon:unit", "number:int", "target:region", "growl:string", 0));
+
+ random_growl(m, findregion(1, 0), 3);
+
+ msg = test_get_last_message(r->msgs);
+ assert_message(tc, msg, "dragon_growl", 4);
+ assert_pointer_parameter(tc, msg, 0, m);
+ assert_int_parameter(tc, msg, 1, 1);
+ assert_pointer_parameter(tc, msg, 2, findregion(1,0));
+ assert_string_parameter(tc, msg, 3, "growl3");
+
test_cleanup();
}
diff --git a/src/move.c b/src/move.c
index e602b073c..e7ca523b5 100644
--- a/src/move.c
+++ b/src/move.c
@@ -677,12 +677,6 @@ int check_ship_allowed(struct ship *sh, const region * r)
}
if (is_freezing(u)) {
- unit *captain = ship_owner(sh);
- if (captain) {
- ADDMSG(&captain->faction->msgs,
- msg_message("detectforbidden", "unit region", u, r));
- }
-
return SA_NO_INSECT;
}
}
@@ -783,9 +777,26 @@ static void msg_to_ship_inmates(ship *sh, unit **firstu, unit **lastu, message *
msg_release(msg);
}
+region * drift_target(ship *sh) {
+ int d, d_offset = rng_int() % MAXDIRECTIONS;
+ region *rnext = NULL;
+ for (d = 0; d != MAXDIRECTIONS; ++d) {
+ region *rn;
+ direction_t dir = (direction_t)((d + d_offset) % MAXDIRECTIONS);
+ rn = rconnect(sh->region, dir);
+ if (rn != NULL && check_ship_allowed(sh, rn) >= 0) {
+ rnext = rn;
+ if (!fval(rnext->terrain, SEA_REGION)) {
+ // prefer drifting towards non-ocean regions
+ break;
+ }
+ }
+ }
+ return rnext;
+}
+
static void drifting_ships(region * r)
{
- direction_t d;
bool drift = config_get_int("rules.ship.drifting", 1) != 0;
double damage_drift = config_get_flt("rules.ship.damage_drift", 0.02);
@@ -796,7 +807,6 @@ static void drifting_ships(region * r)
region *rnext = NULL;
region_list *route = NULL;
unit *firstu = r->units, *lastu = NULL, *captain;
- int d_offset;
direction_t dir = 0;
double ovl;
@@ -831,17 +841,7 @@ static void drifting_ships(region * r)
} 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;
- }
- }
+ rnext = drift_target(sh);
}
if (rnext != NULL) {
@@ -1946,7 +1946,10 @@ sail(unit * u, order * ord, bool move_on_land, region_list ** routep)
reason = check_ship_allowed(sh, next_point);
if (reason < 0) {
/* for some reason or another, we aren't allowed in there.. */
- if (check_leuchtturm(current_point, NULL) || reason == SA_NO_INSECT) {
+ if (reason == SA_NO_INSECT) {
+ ADDMSG(&f->msgs, msg_message("detectforbidden", "unit region", u, sh->region));
+ }
+ else if (check_leuchtturm(current_point, NULL)) {
ADDMSG(&f->msgs, msg_message("sailnolandingstorm", "ship region", sh, next_point));
}
else {
diff --git a/src/move.h b/src/move.h
index 0417dcec5..9c5893629 100644
--- a/src/move.h
+++ b/src/move.h
@@ -77,12 +77,13 @@ extern "C" {
void move_cmd(struct unit * u, struct order * ord, bool move_on_land);
int follow_ship(struct unit * u, struct order * ord);
-#define SA_HARBOUR 2
-#define SA_COAST 1
+#define SA_HARBOUR 1
+#define SA_COAST 0
#define SA_NO_INSECT -1
#define SA_NO_COAST -2
int check_ship_allowed(struct ship *sh, const struct region * r);
+ struct region * drift_target(struct ship *sh);
#ifdef __cplusplus
}
#endif
diff --git a/src/move.test.c b/src/move.test.c
index 4c0a07bc5..e974a7b69 100644
--- a/src/move.test.c
+++ b/src/move.test.c
@@ -35,8 +35,8 @@ static void test_ship_not_allowed_in_coast(CuTest * tc)
ship_type *stype;
test_cleanup();
- ttype = test_create_terrain("glacier", LAND_REGION | ARCTIC_REGION | WALK_INTO | SAIL_INTO);
- otype = test_create_terrain("ocean", SEA_REGION | SAIL_INTO);
+ ttype = test_create_terrain("glacier", LAND_REGION | ARCTIC_REGION | WALK_INTO);
+ otype = test_create_terrain("ocean", SEA_REGION);
stype = test_create_shiptype("derp");
free(stype->coasts);
stype->coasts = (struct terrain_type **)calloc(2, sizeof(struct terrain_type *));
@@ -69,7 +69,7 @@ static void setup_harbor(move_fixture *mf) {
test_cleanup();
- ttype = test_create_terrain("glacier", LAND_REGION | ARCTIC_REGION | WALK_INTO | SAIL_INTO);
+ ttype = test_create_terrain("glacier", LAND_REGION | ARCTIC_REGION | WALK_INTO);
btype = test_create_buildingtype("harbour");
sh = test_create_ship(0, 0);
@@ -232,7 +232,7 @@ static void test_ship_trails(CuTest *tc) {
region_list *route = 0;
test_cleanup();
- otype = test_create_terrain("ocean", SEA_REGION | SAIL_INTO);
+ otype = test_create_terrain("ocean", SEA_REGION);
r1 = test_create_region(0, 0, otype);
r2 = test_create_region(1, 0, otype);
r3 = test_create_region(2, 0, otype);
@@ -298,7 +298,7 @@ void setup_drift (struct drift_fixture *fix) {
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);
+ assert(fix->r);
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);
@@ -498,6 +498,24 @@ static void test_follow_ship_msg(CuTest * tc) {
test_cleanup();
}
+static void test_drifting_ships(CuTest *tc) {
+ ship *sh;
+ region *r1, *r2, *r3;
+ terrain_type *t_ocean, *t_plain;
+ ship_type *st_boat;
+ test_cleanup();
+ t_ocean = test_create_terrain("ocean", SEA_REGION);
+ t_plain = test_create_terrain("plain", LAND_REGION);
+ r1 = test_create_region(0, 0, t_ocean);
+ r2 = test_create_region(1, 0, t_ocean);
+ st_boat = test_create_shiptype("boat");
+ sh = test_create_ship(r1, st_boat);
+ CuAssertPtrEquals(tc, r2, drift_target(sh));
+ r3 = test_create_region(-1, 0, t_plain);
+ CuAssertPtrEquals(tc, r3, drift_target(sh));
+ test_cleanup();
+}
+
CuSuite *get_move_suite(void)
{
CuSuite *suite = CuSuiteNew();
@@ -521,5 +539,6 @@ CuSuite *get_move_suite(void)
SUITE_ADD_TEST(suite, test_ship_ridiculous_overload_no_captain);
SUITE_ADD_TEST(suite, test_ship_damage_overload);
SUITE_ADD_TEST(suite, test_follow_ship_msg);
+ SUITE_ADD_TEST(suite, test_drifting_ships);
return suite;
}
diff --git a/src/piracy.c b/src/piracy.c
index e8864faa9..05a744cb9 100644
--- a/src/piracy.c
+++ b/src/piracy.c
@@ -151,7 +151,7 @@ void piracy_cmd(unit * u, order *ord)
// TODO this could still result in an illegal movement order (through a wall or whatever)
// which will be prevented by move_cmd below
if (rc &&
- ((sh && fval(rc->terrain, SAIL_INTO) && can_takeoff(sh, r, rc))
+ ((sh && !fval(rc->terrain, FORBIDDEN_REGION) && can_takeoff(sh, r, rc))
|| (canswim(u) && fval(rc->terrain, SWIM_INTO) && fval(rc->terrain, SEA_REGION)))) {
for (sh2 = rc->ships; sh2; sh2 = sh2->next) {
diff --git a/src/piracy.test.c b/src/piracy.test.c
index 419f73073..005ce5857 100644
--- a/src/piracy.test.c
+++ b/src/piracy.test.c
@@ -26,7 +26,7 @@ static void setup_piracy(void) {
lang = get_or_create_locale("de");
locale_setstring(lang, directions[D_EAST], "OSTEN");
init_directions(lang);
- test_create_terrain("ocean", SAIL_INTO | SEA_REGION);
+ test_create_terrain("ocean", SEA_REGION);
st_boat = test_create_shiptype("boat");
st_boat->cargo = 1000;
}
@@ -184,7 +184,7 @@ static void test_piracy_cmd_land_to_land(CuTest * tc) {
test_cleanup();
- setup_pirate(&pirate, 0, 0, "boat", &ord, &victim, SAIL_INTO, "boat");
+ setup_pirate(&pirate, 0, 0, "boat", &ord, &victim, SEA_REGION, "boat");
set_level(pirate, SK_SAILING, pirate->ship->type->sumskill);
r = pirate->region;
diff --git a/src/tests.c b/src/tests.c
index a0d54a4cc..fcb34040c 100644
--- a/src/tests.c
+++ b/src/tests.c
@@ -54,7 +54,7 @@ struct region *test_create_region(int x, int y, const terrain_type *terrain)
if (!terrain) {
terrain_type *t = get_or_create_terrain("plain");
t->size = 1000;
- fset(t, LAND_REGION|CAVALRY_REGION|FOREST_REGION|FLY_INTO|WALK_INTO|SAIL_INTO);
+ fset(t, LAND_REGION|CAVALRY_REGION|FOREST_REGION|FLY_INTO|WALK_INTO);
terraform_region(r, t);
}
else {
@@ -196,9 +196,10 @@ ship_type * test_create_shiptype(const char * name)
free(stype->coasts);
}
stype->coasts =
- (terrain_type **)malloc(sizeof(terrain_type *) * 2);
- stype->coasts[0] = test_create_terrain("plain", LAND_REGION | FOREST_REGION | WALK_INTO | CAVALRY_REGION | SAIL_INTO | FLY_INTO);
- stype->coasts[1] = NULL;
+ (terrain_type **)malloc(sizeof(terrain_type *) * 3);
+ stype->coasts[0] = test_create_terrain("plain", LAND_REGION | FOREST_REGION | WALK_INTO | CAVALRY_REGION | FLY_INTO);
+ stype->coasts[1] = test_create_terrain("ocean", SEA_REGION | SWIM_INTO | FLY_INTO);
+ stype->coasts[2] = NULL;
if (default_locale) {
locale_setstring(default_locale, name, name);
}
@@ -322,10 +323,10 @@ 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 | SAIL_INTO | FLY_INTO);
+ t_plain = test_create_terrain("plain", LAND_REGION | FOREST_REGION | WALK_INTO | CAVALRY_REGION | FLY_INTO);
t_plain->size = 1000;
t_plain->max_road = 100;
- t_ocean = test_create_terrain("ocean", SEA_REGION | SAIL_INTO | SWIM_INTO | FLY_INTO);
+ t_ocean = test_create_terrain("ocean", SEA_REGION | SWIM_INTO | FLY_INTO);
t_ocean->size = 0;
island[0] = test_create_region(0, 0, t_plain);
@@ -390,6 +391,29 @@ void test_clear_messages(faction *f) {
}
}
+void assert_message(CuTest * tc, message *msg, char *name, int numpar) {
+ const message_type *mtype = msg->type;
+ assert(mtype);
+
+ CuAssertStrEquals(tc, name, mtype->name);
+ CuAssertIntEquals(tc, numpar, mtype->nparameters);
+}
+
+void assert_pointer_parameter(CuTest * tc, message *msg, int index, void *arg) {
+ const message_type *mtype = (msg)->type;
+ CuAssertIntEquals((tc), VAR_VOIDPTR, mtype->types[(index)]->vtype);CuAssertPtrEquals((tc), (arg), msg->parameters[(index)].v);
+}
+
+void assert_int_parameter(CuTest * tc, message *msg, int index, int arg) {
+ const message_type *mtype = (msg)->type;
+ CuAssertIntEquals((tc), VAR_INT, mtype->types[(index)]->vtype);CuAssertIntEquals((tc), (arg), msg->parameters[(index)].i);
+}
+
+void assert_string_parameter(CuTest * tc, message *msg, int index, const char *arg) {
+ const message_type *mtype = (msg)->type;
+ CuAssertIntEquals((tc), VAR_VOIDPTR, mtype->types[(index)]->vtype);CuAssertStrEquals((tc), (arg), msg->parameters[(index)].v);
+}
+
void disabled_test(void *suite, void (*test)(CuTest *), const char *name) {
(void)test;
fprintf(stderr, "%s: SKIP\n", name);
diff --git a/src/tests.h b/src/tests.h
index 9836c42ab..d18ff6456 100644
--- a/src/tests.h
+++ b/src/tests.h
@@ -54,6 +54,11 @@ extern "C" {
struct message * test_find_messagetype(struct message_list *msgs, const char *name);
struct message * test_get_last_message(struct message_list *mlist);
void test_clear_messages(struct faction *f);
+ void assert_message(struct CuTest * tc, struct message *msg, char *name, int numpar);
+
+ void assert_pointer_parameter(struct CuTest * tc, struct message *msg, int index, void *arg);
+ void assert_int_parameter(struct CuTest * tc, struct message *msg, int index, int arg);
+ void assert_string_parameter(struct CuTest * tc, struct message *msg, int index, const char *arg);
void disabled_test(void *suite, void (*)(struct CuTest *), const char *name);
diff --git a/src/util/log.test.c b/src/util/log.test.c
index 2750d4b1e..95cbefa96 100644
--- a/src/util/log.test.c
+++ b/src/util/log.test.c
@@ -23,10 +23,10 @@ static void test_logging(CuTest * tc)
struct log_t * id2 = log_create(LOG_CPWARNING, str2, log_string);
CuAssertTrue(tc, id1!=id2);
log_warning("Hello %s", "World");
- CuAssertStrEquals(tc, str1, "World");
- CuAssertStrEquals(tc, str2, "World");
log_destroy(id1);
log_destroy(id2);
+ CuAssertStrEquals(tc, "World", str1);
+ CuAssertStrEquals(tc, "World", str2);
}
CuSuite *get_log_suite(void)
diff --git a/src/util/parser.c b/src/util/parser.c
index 63b293e82..741fd573f 100644
--- a/src/util/parser.c
+++ b/src/util/parser.c
@@ -192,7 +192,7 @@ char *parse_token(const char **str, char *lbuf, size_t buflen)
copy = true;
}
if (copy) {
- if (cursor - buflen < lbuf - 1) {
+ if (cursor - buflen < lbuf - len) {
memcpy(cursor, ctoken, len);
cursor += len;
}
diff --git a/src/util/parser.test.c b/src/util/parser.test.c
index 95ef70a17..da0d8dacb 100644
--- a/src/util/parser.test.c
+++ b/src/util/parser.test.c
@@ -1,8 +1,58 @@
#include
#include "parser.h"
+#include
#include
+static void test_parse_token(CuTest *tc) {
+ char lbuf[8];
+ const char *tok;
+ const char *str, *orig;
+
+ orig = str = "SHORT TOKEN";
+ tok = parse_token(&str, lbuf, sizeof(lbuf));
+ CuAssertPtrEquals(tc, (void *)(orig+5), (void *)str);
+ CuAssertStrEquals(tc, "SHORT", tok);
+ tok = parse_token(&str, lbuf, sizeof(lbuf));
+ CuAssertPtrEquals(tc, (void *)(orig + strlen(orig)), (void *)str);
+ CuAssertStrEquals(tc, "TOKEN", tok);
+ tok = parse_token(&str, lbuf, sizeof(lbuf));
+ CuAssertPtrEquals(tc, NULL, (void *)tok);
+}
+
+static void test_parse_token_limit(CuTest *tc) {
+ char lbuf[8];
+ const char *tok;
+ const char *str, *orig;
+
+ orig = str = "LONG_TOKEN";
+ tok = parse_token(&str, lbuf, sizeof(lbuf));
+ CuAssertPtrEquals(tc, (void *)(orig + strlen(orig)), (void *)str);
+ CuAssertStrEquals(tc, tok, "LONG_TO");
+ tok = parse_token(&str, lbuf, sizeof(lbuf));
+ CuAssertPtrEquals(tc, NULL, (void *)tok);
+}
+
+static void test_parse_token_limit_utf8(CuTest *tc) {
+ char lbuf[8];
+ const char *tok;
+ const char *orig = "a\xc3\xa4\xc3\xb6\xc3\xbc\xc3\x9f"; /* auml ouml uuml szlig, 8 bytes long */
+ const char *str = orig+1;
+
+ tok = parse_token(&str, lbuf, sizeof(lbuf));
+ CuAssertPtrEquals(tc, (void *)(orig + strlen(orig)), (void *)str);
+ CuAssertStrEquals(tc, tok, "\xc3\xa4\xc3\xb6\xc3\xbc"); // just three letters fit, 6 bytes long
+ tok = parse_token(&str, lbuf, sizeof(lbuf));
+ CuAssertPtrEquals(tc, NULL, (void *)tok);
+
+ str = orig; // now with an extra byte in the front, maxing out lbuf exactly
+ tok = parse_token(&str, lbuf, sizeof(lbuf));
+ CuAssertPtrEquals(tc, (void *)(orig + strlen(orig)), (void *)str);
+ CuAssertStrEquals(tc, tok, "a\xc3\xa4\xc3\xb6\xc3\xbc");
+ tok = parse_token(&str, lbuf, sizeof(lbuf));
+ CuAssertPtrEquals(tc, NULL, (void *)tok);
+}
+
static void test_gettoken(CuTest *tc) {
char token[128];
init_tokens_str("HELP ONE TWO THREE");
@@ -64,6 +114,9 @@ CuSuite *get_parser_suite(void)
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_atoip);
SUITE_ADD_TEST(suite, test_skip_token);
+ SUITE_ADD_TEST(suite, test_parse_token);
+ SUITE_ADD_TEST(suite, test_parse_token_limit);
+ SUITE_ADD_TEST(suite, test_parse_token_limit_utf8);
SUITE_ADD_TEST(suite, test_gettoken);
SUITE_ADD_TEST(suite, test_gettoken_short);
SUITE_ADD_TEST(suite, test_getintegers);