Merge pull request #355 from ennorehling/feature/battle-test

bug 2149: armor vs. projectile weapons
This commit is contained in:
Enno Rehling 2015-11-03 20:52:51 +01:00
commit 4854a4f765
8 changed files with 221 additions and 94 deletions

View File

@ -1078,6 +1078,81 @@ static int rc_specialdamage(const unit *au, const unit *du, const struct weapon_
return modifier; return modifier;
} }
int calculate_armor(troop dt, const weapon_type *dwtype, const weapon_type *awtype, double *magres) {
static int rule_armor = -1;
fighter *df = dt.fighter;
unit *du = df->unit;
int ar = 0, an, am;
const armor_type *armor = select_armor(dt, false);
const armor_type *shield = select_armor(dt, true);
bool missile = awtype && (awtype->flags&WTF_MISSILE);
if (armor) {
ar += armor->prot;
if (missile && armor->projectile > 0 && chance(armor->projectile)) {
return -1;
}
}
if (shield) {
ar += shield->prot;
if (missile && shield->projectile > 0 && chance(shield->projectile)) {
return -1;
}
}
/* natürliche Rüstung */
an = natural_armor(du);
/* magische Rüstung durch Artefakte oder Sprüche */
/* Momentan nur Trollgürtel und Werwolf-Eigenschaft */
am = select_magicarmor(dt);
if (rule_armor < 0) {
rule_armor = get_param_int(global.parameters, "rules.combat.nat_armor", 0);
}
if (rule_armor == 0) {
/* natürliche Rüstung ist halbkumulativ */
if (ar > 0) {
ar += an / 2;
}
else {
ar = an;
}
}
else {
/* use the higher value, add half the other value */
ar = (ar > an) ? (ar + an / 2) : (an + ar / 2);
}
if (awtype && fval(awtype, WTF_ARMORPIERCING)) {
/* crossbows */
ar /= 2;
}
ar += am;
if (magres) {
/* magic_resistance gib x% Resistenzbonus zurück */
double res = *magres - magic_resistance(du) * 3.0;
if (u_race(du)->battle_flags & BF_EQUIPMENT) {
/* der Effekt von Laen steigt nicht linear */
if (armor && fval(armor, ATF_LAEN))
res *= (1 - armor->magres);
if (shield && fval(shield, ATF_LAEN))
res *= (1 - shield->magres);
if (dwtype)
res *= (1 - dwtype->magres);
}
/* gegen Magie wirkt nur natürliche und magische Rüstung */
ar = an + am;
*magres = res;
}
return ar;
}
bool bool
terminate(troop dt, troop at, int type, const char *damage, bool missile) terminate(troop dt, troop at, int type, const char *damage, bool missile)
{ {
@ -1088,19 +1163,15 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile)
unit *du = df->unit; unit *du = df->unit;
battle *b = df->side->battle; battle *b = df->side->battle;
int heiltrank = 0; int heiltrank = 0;
static int rule_armor = -1;
/* Schild */ /* Schild */
side *ds = df->side; side *ds = df->side;
int hp; int hp, ar;
int ar = 0, an, am;
const armor_type *armor = select_armor(dt, false);
const armor_type *shield = select_armor(dt, true);
const weapon_type *dwtype = NULL; const weapon_type *dwtype = NULL;
const weapon_type *awtype = NULL; const weapon_type *awtype = NULL;
const weapon *weapon; const weapon *weapon;
double res = 0.0;
int rda, sk = 0, sd; int rda, sk = 0, sd;
bool magic = false; bool magic = false;
@ -1138,49 +1209,14 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile)
da += CavalryBonus(au, dt, BONUS_DAMAGE); da += CavalryBonus(au, dt, BONUS_DAMAGE);
} }
if (armor) { ar = calculate_armor(dt, dwtype, awtype, magic ? &res : 0);
ar += armor->prot; if (ar < 0) {
if (armor->projectile > 0 && chance(armor->projectile)) {
return false; return false;
} }
}
if (shield) {
ar += shield->prot;
if (shield->projectile > 0 && chance(shield->projectile)) {
return false;
}
}
/* natürliche Rüstung */ if (magic) {
an = natural_armor(du); da = (int)(_max(da * res, 0));
/* magische Rüstung durch Artefakte oder Sprüche */
/* Momentan nur Trollgürtel und Werwolf-Eigenschaft */
am = select_magicarmor(dt);
if (rule_armor < 0) {
rule_armor = get_param_int(global.parameters, "rules.combat.nat_armor", 0);
} }
if (rule_armor == 0) {
/* natürliche Rüstung ist halbkumulativ */
if (ar > 0) {
ar += an / 2;
}
else {
ar = an;
}
}
else {
/* use the higher value, add half the other value */
ar = (ar > an) ? (ar + an / 2) : (an + ar / 2);
}
if (awtype && fval(awtype, WTF_ARMORPIERCING)) {
/* crossbows */
ar /= 2;
}
ar += am;
if (type != AT_COMBATSPELL && type != AT_SPELL) { if (type != AT_COMBATSPELL && type != AT_SPELL) {
if (damage_rules & DAMAGE_CRITICAL) { if (damage_rules & DAMAGE_CRITICAL) {
@ -1218,30 +1254,6 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile)
} }
} }
if (magic) {
/* Magischer Schaden durch Spruch oder magische Waffe */
double res = 1.0;
/* magic_resistance gib x% Resistenzbonus zurück */
res -= magic_resistance(du) * 3.0;
if (u_race(du)->battle_flags & BF_EQUIPMENT) {
/* der Effekt von Laen steigt nicht linear */
if (armor && fval(armor, ATF_LAEN))
res *= (1 - armor->magres);
if (shield && fval(shield, ATF_LAEN))
res *= (1 - shield->magres);
if (dwtype)
res *= (1 - dwtype->magres);
}
if (res > 0) {
da = (int)(_max(da * res, 0));
}
/* gegen Magie wirkt nur natürliche und magische Rüstung */
ar = an + am;
}
rda = _max(da - ar, 0); rda = _max(da - ar, 0);
if ((u_race(du)->battle_flags & BF_INV_NONMAGIC) && !magic) if ((u_race(du)->battle_flags & BF_INV_NONMAGIC) && !magic)

View File

@ -241,9 +241,10 @@ extern "C" {
extern troop select_ally(struct fighter *af, int minrow, int maxrow, extern troop select_ally(struct fighter *af, int minrow, int maxrow,
int allytype); int allytype);
extern int count_enemies(struct battle *b, const struct fighter *af, int count_enemies(struct battle *b, const struct fighter *af,
int minrow, int maxrow, int select); int minrow, int maxrow, int select);
extern bool terminate(troop dt, troop at, int type, const char *damage, int calculate_armor(troop dt, const struct weapon_type *dwtype, const struct weapon_type *awtype, double *magres);
bool terminate(troop dt, troop at, int type, const char *damage,
bool missile); bool missile);
extern void message_all(battle * b, struct message *m); extern void message_all(battle * b, struct message *m);
extern int hits(troop at, troop dt, weapon * awp); extern int hits(troop at, troop dt, weapon * awp);

View File

@ -204,6 +204,117 @@ static void test_building_defence_bonus(CuTest * tc)
test_cleanup(); test_cleanup();
} }
fighter *setup_fighter(battle **bp, unit *u) {
battle *b;
*bp = b = make_battle(u->region);
return make_fighter(b, u, make_side(b, u->faction, 0, 0, 0), false);
}
static void test_calculate_armor(CuTest * tc)
{
troop dt;
battle *b;
region *r;
unit *du;
weapon_type *wtype;
armor_type *ashield, *achain;
item_type *ibelt, *ishield, *ichain;
race *rc;
double magres = 0.0;
test_cleanup();
r = test_create_region(0, 0, 0);
ibelt = it_get_or_create(rt_get_or_create("trollbelt"));
ishield = it_get_or_create(rt_get_or_create("shield"));
ashield = new_armortype(ishield, 0.0, 0.5, 1, ATF_SHIELD);
ichain = it_get_or_create(rt_get_or_create("chainmail"));
achain = new_armortype(ichain, 0.0, 0.5, 3, ATF_NONE);
wtype = new_weapontype(it_get_or_create(rt_get_or_create("sword")), 0, 0.5, 0, 0, 0, 0, SK_MELEE, 1);
rc = test_create_race("human");
du = test_create_unit(test_create_faction(rc), r);
dt.index = 0;
dt.fighter = setup_fighter(&b, du);
CuAssertIntEquals_Msg(tc, "default ac", 0, calculate_armor(dt, 0, 0, &magres));
CuAssertDblEquals_Msg(tc, "magres unmodified", 0.0, magres, 0.01);
free_battle(b);
i_change(&du->items, ibelt, 1);
dt.fighter = setup_fighter(&b, du);
CuAssertIntEquals_Msg(tc, "magical armor", 1, calculate_armor(dt, 0, 0, 0));
rc->armor = 2;
CuAssertIntEquals_Msg(tc, "natural armor", 3, calculate_armor(dt, 0, 0, 0));
rc->armor = 0;
free_battle(b);
i_change(&du->items, ishield, 1);
i_change(&du->items, ichain, 1);
dt.fighter = setup_fighter(&b, du);
CuAssertIntEquals_Msg(tc, "require BF_EQUIPMENT", 1, calculate_armor(dt, 0, 0, 0));
free_battle(b);
rc->battle_flags |= BF_EQUIPMENT;
dt.fighter = setup_fighter(&b, du);
CuAssertIntEquals_Msg(tc, "stack equipment rc", 5, calculate_armor(dt, 0, 0, 0));
rc->armor = 2;
CuAssertIntEquals_Msg(tc, "natural armor adds 50%", 6, calculate_armor(dt, 0, 0, 0));
wtype->flags = WTF_NONE;
CuAssertIntEquals_Msg(tc, "regular weapon has no effect", 6, calculate_armor(dt, 0, wtype, 0));
wtype->flags = WTF_ARMORPIERCING;
CuAssertIntEquals_Msg(tc, "armor piercing weapon", 3, calculate_armor(dt, 0, wtype, 0));
wtype->flags = WTF_NONE;
CuAssertIntEquals_Msg(tc, "magical attack", 3, calculate_armor(dt, 0, 0, &magres));
CuAssertDblEquals_Msg(tc, "magres unmodified", 0.0, magres, 0.01);
ashield->flags |= ATF_LAEN;
achain->flags |= ATF_LAEN;
magres = 1.0;
CuAssertIntEquals_Msg(tc, "laen armor", 3, calculate_armor(dt, 0, 0, &magres));
CuAssertDblEquals_Msg(tc, "laen magres bonus", 0.25, magres, 0.01);
free_battle(b);
test_cleanup();
}
static void test_projectile_armor(CuTest * tc)
{
troop dt;
battle *b;
region *r;
unit *du;
weapon_type *wtype;
armor_type *ashield, *achain;
item_type *ishield, *ichain;
race *rc;
test_cleanup();
r = test_create_region(0, 0, 0);
ishield = it_get_or_create(rt_get_or_create("shield"));
ashield = new_armortype(ishield, 0.0, 0.5, 1, ATF_SHIELD);
ichain = it_get_or_create(rt_get_or_create("chainmail"));
achain = new_armortype(ichain, 0.0, 0.5, 3, ATF_NONE);
wtype = new_weapontype(it_get_or_create(rt_get_or_create("sword")), 0, 0.5, 0, 0, 0, 0, SK_MELEE, 1);
rc = test_create_race("human");
rc->battle_flags |= BF_EQUIPMENT;
du = test_create_unit(test_create_faction(rc), r);
dt.index = 0;
i_change(&du->items, ishield, 1);
i_change(&du->items, ichain, 1);
dt.fighter = setup_fighter(&b, du);
wtype->flags = WTF_MISSILE;
achain->projectile = 1.0;
CuAssertIntEquals_Msg(tc, "projectile armor", -1, calculate_armor(dt, 0, wtype, 0));
achain->projectile = 0.0;
ashield->projectile = 1.0;
CuAssertIntEquals_Msg(tc, "projectile shield", -1, calculate_armor(dt, 0, wtype, 0));
wtype->flags = WTF_NONE;
CuAssertIntEquals_Msg(tc, "no projectiles", 4, calculate_armor(dt, 0, wtype, 0));
free_battle(b);
test_cleanup();
}
CuSuite *get_battle_suite(void) CuSuite *get_battle_suite(void)
{ {
CuSuite *suite = CuSuiteNew(); CuSuite *suite = CuSuiteNew();
@ -212,5 +323,7 @@ CuSuite *get_battle_suite(void)
SUITE_ADD_TEST(suite, test_attackers_get_no_building_bonus); SUITE_ADD_TEST(suite, test_attackers_get_no_building_bonus);
SUITE_ADD_TEST(suite, test_building_bonus_respects_size); SUITE_ADD_TEST(suite, test_building_bonus_respects_size);
SUITE_ADD_TEST(suite, test_building_defence_bonus); SUITE_ADD_TEST(suite, test_building_defence_bonus);
SUITE_ADD_TEST(suite, test_calculate_armor);
SUITE_ADD_TEST(suite, test_projectile_armor);
return suite; return suite;
} }

View File

@ -547,25 +547,25 @@ void register_tolua_helpers(void)
at_register(&at_direction); at_register(&at_direction);
at_register(&at_building_action); at_register(&at_building_action);
register_function((pf_generic)& lua_building_taxes, register_function((pf_generic)lua_building_taxes,
TOLUA_CAST "lua_building_taxes"); TOLUA_CAST "lua_building_taxes");
register_function((pf_generic)& lua_agebuilding, register_function((pf_generic)lua_agebuilding,
TOLUA_CAST "lua_agebuilding"); TOLUA_CAST "lua_agebuilding");
register_function((pf_generic)& lua_callspell, TOLUA_CAST "lua_castspell"); register_function((pf_generic)lua_callspell, TOLUA_CAST "lua_castspell");
register_function((pf_generic)& lua_initfamiliar, register_function((pf_generic)lua_initfamiliar,
TOLUA_CAST "lua_initfamiliar"); TOLUA_CAST "lua_initfamiliar");
register_item_use(&lua_useitem, TOLUA_CAST "lua_useitem"); register_item_use(&lua_useitem, TOLUA_CAST "lua_useitem");
register_function((pf_generic)& lua_getresource, register_function((pf_generic)lua_getresource,
TOLUA_CAST "lua_getresource"); TOLUA_CAST "lua_getresource");
register_function((pf_generic)& lua_canuse_item, register_function((pf_generic)lua_canuse_item,
TOLUA_CAST "lua_canuse_item"); TOLUA_CAST "lua_canuse_item");
register_function((pf_generic)& lua_changeresource, register_function((pf_generic)lua_changeresource,
TOLUA_CAST "lua_changeresource"); TOLUA_CAST "lua_changeresource");
register_function((pf_generic)& lua_equipmentcallback, register_function((pf_generic)lua_equipmentcallback,
TOLUA_CAST "lua_equip"); TOLUA_CAST "lua_equip");
register_function((pf_generic)& lua_wage, TOLUA_CAST "lua_wage"); register_function((pf_generic)lua_wage, TOLUA_CAST "lua_wage");
register_function((pf_generic)& lua_maintenance, register_function((pf_generic)lua_maintenance,
TOLUA_CAST "lua_maintenance"); TOLUA_CAST "lua_maintenance");
register_function((pf_generic)produce_resource, register_function((pf_generic)produce_resource,

View File

@ -357,14 +357,14 @@ static int meropis_building_protection(building * b, unit * u)
void register_buildings(void) void register_buildings(void)
{ {
register_function((pf_generic)& building_protection, register_function((pf_generic)building_protection,
"building_protection"); "building_protection");
register_function((pf_generic)& meropis_building_protection, register_function((pf_generic)meropis_building_protection,
"meropis_building_protection"); "meropis_building_protection");
register_function((pf_generic)& init_smithy, "init_smithy"); register_function((pf_generic)init_smithy, "init_smithy");
register_function((pf_generic)& castle_name, "castle_name"); register_function((pf_generic)castle_name, "castle_name");
register_function((pf_generic)& castle_name_2, "castle_name_2"); register_function((pf_generic)castle_name_2, "castle_name_2");
register_function((pf_generic)& fort_name, "fort_name"); register_function((pf_generic)fort_name, "fort_name");
} }
void write_building_reference(const struct building *b, struct storage *store) void write_building_reference(const struct building *b, struct storage *store)

View File

@ -1591,7 +1591,7 @@ void attrib_init(void)
register_bordertype(&bt_illusionwall); register_bordertype(&bt_illusionwall);
register_bordertype(&bt_road); register_bordertype(&bt_road);
register_function((pf_generic)& minimum_wage, "minimum_wage"); register_function((pf_generic)minimum_wage, "minimum_wage");
at_register(&at_germs); at_register(&at_germs);

View File

@ -277,7 +277,7 @@ weapon_type *new_weapontype(item_type * itype,
weapon_type *wtype; weapon_type *wtype;
assert(minskill > 0); assert(minskill > 0);
assert(resource2weapon(itype->rtype) == NULL); assert(itype && (!itype->rtype || !resource2weapon(itype->rtype)));
wtype = calloc(sizeof(weapon_type), 1); wtype = calloc(sizeof(weapon_type), 1);
if (damage) { if (damage) {

View File

@ -1116,7 +1116,6 @@ double magic_resistance(unit * target)
{ {
attrib *a; attrib *a;
curse *c; curse *c;
int n;
const curse_type * ct_goodresist = 0, *ct_badresist = 0; const curse_type * ct_goodresist = 0, *ct_badresist = 0;
const resource_type *rtype; const resource_type *rtype;
double probability = u_race(target)->magres; double probability = u_race(target)->magres;
@ -1133,10 +1132,12 @@ double magic_resistance(unit * target)
/* Unicorn +10 */ /* Unicorn +10 */
rtype = get_resourcetype(R_UNICORN); rtype = get_resourcetype(R_UNICORN);
n = i_get(target->items, rtype->itype); if (rtype) {
int n = i_get(target->items, rtype->itype);
if (n) { if (n) {
probability += n * 0.1 / target->number; probability += n * 0.1 / target->number;
} }
}
/* Auswirkungen von Zaubern auf der Region */ /* Auswirkungen von Zaubern auf der Region */
a = a_find(target->region->attribs, &at_curse); a = a_find(target->region->attribs, &at_curse);