Merge branch 'stm2-healing_potion' into develop

This commit is contained in:
Enno Rehling 2016-06-17 21:53:56 +02:00
commit defc5c19e7
25 changed files with 380 additions and 192 deletions

View File

@ -7574,4 +7574,22 @@
</string>
</namespace>
<string name="growl0">
<text locale="de">Groaamm...</text>
</string>
<string name="growl1">
<text locale="de">Tschrrrk...</text>
<text locale="en">Tshrrrk...</text>
</string>
<string name="growl2">
<text locale="de">Schhhhh...</text>
<text locale="en">Shhhhhh...</text>
</string>
<string name="growl3">
<text locale="de">Roaarrr...</text>
</string>
<string name="growl4">
<text locale="de">Chrrr...</text>
</string>
</strings>

View File

@ -7293,8 +7293,8 @@
<type>
<arg name="unit" type="unit"/>
</type>
<text locale="de">"$unit($unit) konnte durch einen Heiltrank überleben."</text>
<text locale="en">"$unit($unit) was saved by a healing potion."</text>
<text locale="de">"Eine Person von $unit($unit) konnte durch einen Heiltrank überleben."</text>
<text locale="en">"A fighter of $unit($unit) was saved by a healing potion."</text>
</message>
<message name="battle::tactics_lost" section="battle">
<type>
@ -8441,4 +8441,15 @@
<text locale="en">"$unit($unit) in $region($region): '$order($command)' - Heroes cannot recruit."</text>
</message>
<message name="dragon_growl" section="mail">
<type>
<arg name="dragon" type="unit"/>
<arg name="number" type="int"/>
<arg name="target" type="region"/>
<arg name="growl" type="string"/>
</type>
<text locale="de">"$unit($dragon): \"$localize($growl) $if($eq($number,1), "Ich rieche", "Wir riechen") etwas in $region($target)\"."</text>
<text locale="en">"$unit($dragon): \"$localize($growl) $if($eq($number,1), "I smell", "We smell") something in $region($target)\"."</text>
</message>
</messages>

View File

@ -1,7 +1,6 @@
<?xml version="1.0"?>
<ships>
<ship name="trireme" range="7" storm="1.00" damage="1.00" cargo="200000" cptskill="4" minskill="1" sumskill="120" opensea="yes">
<coast terrain="ocean"/>
<coast terrain="plain"/>
<construction skill="shipcraft" minskill="4" maxsize="200" reqsize="1">
<requirement type="log" quantity="1"/>
@ -9,7 +8,6 @@
</ship>
<ship name="caravel" range="5" storm="1.00" damage="1.00" cargo="300000" cptskill="3" minskill="1" sumskill="30" opensea="yes">
<coast terrain="ocean"/>
<coast terrain="plain"/>
<construction skill="shipcraft" minskill="3" maxsize="250" reqsize="1">
<requirement type="log" quantity="1"/>
@ -17,7 +15,6 @@
</ship>
<ship name="dragonship" range="5" storm="1.00" damage="1.00" cargo="100000" cptskill="2" minskill="1" sumskill="50" opensea="yes">
<coast terrain="ocean"/>
<coast terrain="plain"/>
<construction skill="shipcraft" minskill="2" maxsize="100" reqsize="1">
<requirement type="log" quantity="1"/>
@ -25,7 +22,6 @@
</ship>
<ship name="longboat" range="3" storm="1.00" damage="1.00" cargo="50000" cptskill="1" minskill="1" sumskill="10" opensea="yes">
<coast terrain="ocean"/>
<coast terrain="plain"/>
<construction skill="shipcraft" minskill="1" maxsize="50" reqsize="1">
<requirement type="log" quantity="1"/>
@ -33,7 +29,6 @@
</ship>
<ship name="balloon" range="2" storm="1.00" damage="1.00" cargo="5000" cptskill="6" minskill="6" sumskill="6" opensea="yes" fly="yes">
<coast terrain="ocean"/>
<coast terrain="plain"/>
<coast terrain="swamp"/>
<coast terrain="desert"/>
@ -48,7 +43,6 @@
</ship>
<ship name="boat" range="2" storm="1.00" damage="1.00" cargo="5000" cptskill="1" minskill="1" sumskill="2" opensea="yes">
<coast terrain="ocean"/>
<coast terrain="plain"/>
<coast terrain="swamp"/>
<coast terrain="desert"/>
@ -65,7 +59,6 @@
</ship>
<ship name="flyingcarpet" range="3" storm="1.00" damage="1.00" cargo="50000" cptskill="6" minskill="6" sumskill="10" opensea="yes" fly="yes">
<coast terrain="ocean"/>
<coast terrain="plain"/>
<coast terrain="swamp"/>
<coast terrain="desert"/>

View File

@ -1,7 +1,6 @@
<?xml version="1.0"?>
<ships>
<ship name="canoe" nocoast="true" range="3" fishing="20" storm="1.00" damage="1.00" cabins="2" cargo="2000" cptskill="1" minskill="1" sumskill="2" opensea="no">
<coast terrain="ocean"/>
<coast terrain="plain"/>
<coast terrain="packice"/>
<coast terrain="swamp"/>
@ -17,7 +16,6 @@
</ship>
<ship name="raft" range="1" fishing="20" storm="1.00" damage="1.00" cabins="5" cargo="50000" cptskill="1" minskill="1" sumskill="5" opensea="no">
<coast terrain="ocean"/>
<coast terrain="plain"/>
<coast terrain="packice"/>
<coast terrain="swamp"/>
@ -28,7 +26,6 @@
</ship>
<ship name="cutter" range="2" fishing="20" storm="1.00" damage="1.00" cabins="5" cargo="5500" cptskill="2" minskill="1" sumskill="5" opensea="yes">
<coast terrain="ocean"/>
<coast terrain="plain"/>
<coast terrain="packice"/>
<coast terrain="swamp"/>
@ -44,7 +41,6 @@
</ship>
<ship name="barge" range="3" fishing="20" storm="1.00" damage="1.00" cabins="11" cargo="5000" cptskill="2" minskill="1" sumskill="5" opensea="no">
<coast terrain="ocean"/>
<coast terrain="plain"/>
<coast terrain="packice"/>
<coast terrain="swamp"/>
@ -61,7 +57,6 @@
<ship name="royalbarge" range="5" storm="0.25" damage="1.00" cabins="11" cargo="5000" cptskill="5" minskill="1" sumskill="10" opensea="no">
<coast terrain="ocean"/>
<coast terrain="plain"/>
<coast terrain="packice"/>
<coast terrain="swamp"/>
@ -78,7 +73,6 @@
</ship>
<ship name="catamaran" range="7" storm="0.25" damage="1.00" cabins="22" cargo="10000" cptskill="7" minskill="1" sumskill="20" opensea="yes">
<coast terrain="ocean"/>
<coast terrain="plain"/>
<coast terrain="packice"/>
<construction skill="shipcraft" minskill="8" maxsize="30" reqsize="1">
@ -89,7 +83,6 @@
<ship name="cog" range="4" storm="0.50" damage="1.00" cabins="50" cargo="200000" cptskill="4" minskill="1" sumskill="20" opensea="yes">
<coast terrain="ocean"/>
<coast terrain="plain"/>
<coast terrain="packice"/>
<construction skill="shipcraft" minskill="4" maxsize="100" reqsize="1">
@ -98,7 +91,6 @@
</ship>
<ship name="caravel" range="4" storm="0.50" damage="1.00" cabins="150" cargo="600000" cptskill="6" minskill="1" sumskill="30" opensea="yes">
<coast terrain="ocean"/>
<coast terrain="plain"/>
<coast terrain="packice"/>
<construction skill="shipcraft" minskill="6" maxsize="300" reqsize="1">
@ -109,7 +101,6 @@
<ship name="frigate" range="4" storm="1.00" damage="1.00" cabins="110" cargo="100000" cptskill="5" minskill="1" sumskill="40" opensea="yes">
<modifier type="defense" value="+2"/>
<coast terrain="ocean"/>
<coast terrain="plain"/>
<coast terrain="packice"/>
<construction skill="shipcraft" minskill="5" maxsize="100" reqsize="1">
@ -120,7 +111,6 @@
<ship name="galleon" range="4" storm="1.00" damage="1.00" cabins="310" cargo="300000" cptskill="7" minskill="1" sumskill="60" opensea="yes">
<modifier type="defense" value="+2"/>
<coast terrain="ocean"/>
<coast terrain="plain"/>
<coast terrain="packice"/>
<construction skill="shipcraft" minskill="7" maxsize="300" reqsize="1">
@ -133,7 +123,6 @@
<ship name="dragonship" range="6" storm="1.00" damage="1.00" cabins="110" cargo="50000" cptskill="5" minskill="1" sumskill="60" opensea="yes">
<modifier type="attack" value="+1"/>
<modifier type="tactics" factor="2.00"/>
<coast terrain="ocean"/>
<coast terrain="plain"/>
<coast terrain="packice"/>
<construction skill="shipcraft" minskill="5" maxsize="100" reqsize="1">
@ -145,7 +134,6 @@
<ship name="trireme" range="6" storm="1.00" damage="1.00" cabins="310" cargo="150000" cptskill="7" minskill="1" sumskill="90" opensea="yes">
<modifier type="attack" value="+1"/>
<modifier type="tactics" factor="2.00"/>
<coast terrain="ocean"/>
<coast terrain="plain"/>
<coast terrain="packice"/>
<construction skill="shipcraft" minskill="7" maxsize="300" reqsize="1">

View File

@ -1,6 +1,5 @@
<?xml version="1.0" ?>
<ship name="boat" range="2" storm="1.00" damage="1.00" cargo="5000" cptskill="1" minskill="1" sumskill="2" opensea="yes">
<coast terrain="ocean"/>
<coast terrain="plain"/>
<coast terrain="swamp"/>
<coast terrain="desert"/>

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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 */

View File

@ -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);

View File

@ -27,6 +27,7 @@
#include <assert.h>
#include <stdio.h>
#include <string.h>
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);

View File

@ -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);
}

View File

@ -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 {

View File

@ -12,10 +12,13 @@
#include "monster.h"
#include "guard.h"
#include "reports.h"
#include "skill.h"
#include "study.h"
#include <util/language.h>
#include <util/message.h>
#include <util/nrmessage.h>
#include <CuTest.h>
#include <tests.h>
@ -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();
}

View File

@ -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 {

View File

@ -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

View File

@ -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;
}

View File

@ -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) {

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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;
}

View File

@ -1,8 +1,58 @@
#include <platform.h>
#include "parser.h"
#include <string.h>
#include <CuTest.h>
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);