diff --git a/src/battle.c b/src/battle.c index f4d3bc5a7..853c908d7 100644 --- a/src/battle.c +++ b/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)) { - return false; - } - } - if (shield) { - ar += shield->prot; - if (shield->projectile > 0 && chance(shield->projectile)) { - return false; - } + ar = calculate_armor(dt, dwtype, awtype, magic ? &res : 0); + if (ar < 0) { + 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) diff --git a/src/battle.h b/src/battle.h index 618850d31..36f4898b7 100644 --- a/src/battle.h +++ b/src/battle.h @@ -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); diff --git a/src/battle.test.c b/src/battle.test.c index 264e9c0be..8d8f83b8a 100644 --- a/src/battle.test.c +++ b/src/battle.test.c @@ -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; } diff --git a/src/helpers.c b/src/helpers.c index 29038afb3..a81a91679 100644 --- a/src/helpers.c +++ b/src/helpers.c @@ -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, diff --git a/src/kernel/building.c b/src/kernel/building.c index c17830e5d..6f97efad0 100644 --- a/src/kernel/building.c +++ b/src/kernel/building.c @@ -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) diff --git a/src/kernel/config.c b/src/kernel/config.c index e6a2b9b09..69d09b040 100644 --- a/src/kernel/config.c +++ b/src/kernel/config.c @@ -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); diff --git a/src/kernel/item.c b/src/kernel/item.c index 63ba87a53..ca6c2f32d 100644 --- a/src/kernel/item.c +++ b/src/kernel/item.c @@ -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) { diff --git a/src/magic.c b/src/magic.c index e0a5ca921..a9aa682ec 100644 --- a/src/magic.c +++ b/src/magic.c @@ -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,9 +1132,11 @@ double magic_resistance(unit * target) /* Unicorn +10 */ rtype = get_resourcetype(R_UNICORN); - n = i_get(target->items, rtype->itype); - if (n) { - probability += n * 0.1 / target->number; + if (rtype) { + int n = i_get(target->items, rtype->itype); + if (n) { + probability += n * 0.1 / target->number; + } } /* Auswirkungen von Zaubern auf der Region */