forked from github/server
Merge pull request #355 from ennorehling/feature/battle-test
bug 2149: armor vs. projectile weapons
This commit is contained in:
commit
4854a4f765
150
src/battle.c
150
src/battle.c
|
@ -1078,6 +1078,81 @@ static int rc_specialdamage(const unit *au, const unit *du, const struct weapon_
|
|||
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
|
||||
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;
|
||||
battle *b = df->side->battle;
|
||||
int heiltrank = 0;
|
||||
static int rule_armor = -1;
|
||||
|
||||
/* Schild */
|
||||
side *ds = df->side;
|
||||
int hp;
|
||||
|
||||
int ar = 0, an, am;
|
||||
const armor_type *armor = select_armor(dt, false);
|
||||
const armor_type *shield = select_armor(dt, true);
|
||||
int hp, ar;
|
||||
|
||||
const weapon_type *dwtype = NULL;
|
||||
const weapon_type *awtype = NULL;
|
||||
const weapon *weapon;
|
||||
double res = 0.0;
|
||||
|
||||
int rda, sk = 0, sd;
|
||||
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);
|
||||
}
|
||||
|
||||
if (armor) {
|
||||
ar += armor->prot;
|
||||
if (armor->projectile > 0 && chance(armor->projectile)) {
|
||||
ar = calculate_armor(dt, dwtype, awtype, magic ? &res : 0);
|
||||
if (ar < 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (shield) {
|
||||
ar += shield->prot;
|
||||
if (shield->projectile > 0 && chance(shield->projectile)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 (magic) {
|
||||
da = (int)(_max(da * res, 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 (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);
|
||||
|
||||
if ((u_race(du)->battle_flags & BF_INV_NONMAGIC) && !magic)
|
||||
|
|
|
@ -241,9 +241,10 @@ extern "C" {
|
|||
extern troop select_ally(struct fighter *af, int minrow, int maxrow,
|
||||
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);
|
||||
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);
|
||||
extern void message_all(battle * b, struct message *m);
|
||||
extern int hits(troop at, troop dt, weapon * awp);
|
||||
|
|
|
@ -204,6 +204,117 @@ static void test_building_defence_bonus(CuTest * tc)
|
|||
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 *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_building_bonus_respects_size);
|
||||
SUITE_ADD_TEST(suite, test_building_defence_bonus);
|
||||
SUITE_ADD_TEST(suite, test_calculate_armor);
|
||||
SUITE_ADD_TEST(suite, test_projectile_armor);
|
||||
return suite;
|
||||
}
|
||||
|
|
|
@ -547,25 +547,25 @@ void register_tolua_helpers(void)
|
|||
at_register(&at_direction);
|
||||
at_register(&at_building_action);
|
||||
|
||||
register_function((pf_generic)& lua_building_taxes,
|
||||
register_function((pf_generic)lua_building_taxes,
|
||||
TOLUA_CAST "lua_building_taxes");
|
||||
register_function((pf_generic)& lua_agebuilding,
|
||||
register_function((pf_generic)lua_agebuilding,
|
||||
TOLUA_CAST "lua_agebuilding");
|
||||
register_function((pf_generic)& lua_callspell, TOLUA_CAST "lua_castspell");
|
||||
register_function((pf_generic)& lua_initfamiliar,
|
||||
register_function((pf_generic)lua_callspell, TOLUA_CAST "lua_castspell");
|
||||
register_function((pf_generic)lua_initfamiliar,
|
||||
TOLUA_CAST "lua_initfamiliar");
|
||||
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");
|
||||
register_function((pf_generic)& lua_canuse_item,
|
||||
register_function((pf_generic)lua_canuse_item,
|
||||
TOLUA_CAST "lua_canuse_item");
|
||||
register_function((pf_generic)& lua_changeresource,
|
||||
register_function((pf_generic)lua_changeresource,
|
||||
TOLUA_CAST "lua_changeresource");
|
||||
register_function((pf_generic)& lua_equipmentcallback,
|
||||
register_function((pf_generic)lua_equipmentcallback,
|
||||
TOLUA_CAST "lua_equip");
|
||||
|
||||
register_function((pf_generic)& lua_wage, TOLUA_CAST "lua_wage");
|
||||
register_function((pf_generic)& lua_maintenance,
|
||||
register_function((pf_generic)lua_wage, TOLUA_CAST "lua_wage");
|
||||
register_function((pf_generic)lua_maintenance,
|
||||
TOLUA_CAST "lua_maintenance");
|
||||
|
||||
register_function((pf_generic)produce_resource,
|
||||
|
|
|
@ -357,14 +357,14 @@ static int meropis_building_protection(building * b, unit * u)
|
|||
|
||||
void register_buildings(void)
|
||||
{
|
||||
register_function((pf_generic)& building_protection,
|
||||
register_function((pf_generic)building_protection,
|
||||
"building_protection");
|
||||
register_function((pf_generic)& meropis_building_protection,
|
||||
register_function((pf_generic)meropis_building_protection,
|
||||
"meropis_building_protection");
|
||||
register_function((pf_generic)& init_smithy, "init_smithy");
|
||||
register_function((pf_generic)& castle_name, "castle_name");
|
||||
register_function((pf_generic)& castle_name_2, "castle_name_2");
|
||||
register_function((pf_generic)& fort_name, "fort_name");
|
||||
register_function((pf_generic)init_smithy, "init_smithy");
|
||||
register_function((pf_generic)castle_name, "castle_name");
|
||||
register_function((pf_generic)castle_name_2, "castle_name_2");
|
||||
register_function((pf_generic)fort_name, "fort_name");
|
||||
}
|
||||
|
||||
void write_building_reference(const struct building *b, struct storage *store)
|
||||
|
|
|
@ -1591,7 +1591,7 @@ void attrib_init(void)
|
|||
register_bordertype(&bt_illusionwall);
|
||||
register_bordertype(&bt_road);
|
||||
|
||||
register_function((pf_generic)& minimum_wage, "minimum_wage");
|
||||
register_function((pf_generic)minimum_wage, "minimum_wage");
|
||||
|
||||
at_register(&at_germs);
|
||||
|
||||
|
|
|
@ -277,7 +277,7 @@ weapon_type *new_weapontype(item_type * itype,
|
|||
weapon_type *wtype;
|
||||
|
||||
assert(minskill > 0);
|
||||
assert(resource2weapon(itype->rtype) == NULL);
|
||||
assert(itype && (!itype->rtype || !resource2weapon(itype->rtype)));
|
||||
|
||||
wtype = calloc(sizeof(weapon_type), 1);
|
||||
if (damage) {
|
||||
|
|
|
@ -1116,7 +1116,6 @@ double magic_resistance(unit * target)
|
|||
{
|
||||
attrib *a;
|
||||
curse *c;
|
||||
int n;
|
||||
const curse_type * ct_goodresist = 0, *ct_badresist = 0;
|
||||
const resource_type *rtype;
|
||||
double probability = u_race(target)->magres;
|
||||
|
@ -1133,10 +1132,12 @@ double magic_resistance(unit * target)
|
|||
|
||||
/* Unicorn +10 */
|
||||
rtype = get_resourcetype(R_UNICORN);
|
||||
n = i_get(target->items, rtype->itype);
|
||||
if (rtype) {
|
||||
int n = i_get(target->items, rtype->itype);
|
||||
if (n) {
|
||||
probability += n * 0.1 / target->number;
|
||||
}
|
||||
}
|
||||
|
||||
/* Auswirkungen von Zaubern auf der Region */
|
||||
a = a_find(target->region->attribs, &at_curse);
|
||||
|
|
Loading…
Reference in New Issue