From 91a6324a977ad005ba9ed457de7791b143531d24 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 3 Nov 2015 13:24:58 +0100 Subject: [PATCH 1/6] cleanup: functions do not need to be dereferenced with an & in C. --- src/helpers.c | 20 ++++++++++---------- src/kernel/building.c | 12 ++++++------ src/kernel/config.c | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) 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); From 15ff621cbb7d64aac19bc0508de4f42fca87799d Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 3 Nov 2015 14:22:50 +0100 Subject: [PATCH 2/6] refactor: extract damage calculation from terminate start writing tests (WIP) --- src/battle.c | 166 ++++++++++++++++++++++++---------------------- src/battle.h | 5 +- src/battle.test.c | 26 ++++++++ 3 files changed, 116 insertions(+), 81 deletions(-) diff --git a/src/battle.c b/src/battle.c index f4d3bc5a7..cf8cf9b35 100644 --- a/src/battle.c +++ b/src/battle.c @@ -1078,66 +1078,14 @@ static int rc_specialdamage(const unit *au, const unit *du, const struct weapon_ return modifier; } -bool -terminate(troop dt, troop at, int type, const char *damage, bool missile) -{ - item **pitm; - fighter *df = dt.fighter; - fighter *af = at.fighter; - unit *au = af->unit; - unit *du = df->unit; - battle *b = df->side->battle; - int heiltrank = 0; +int calculate_armor(troop dt, const weapon_type *dwtype, const weapon_type *awtype, double *magres) { static int rule_armor = -1; - - /* Schild */ - side *ds = df->side; - int hp; - + 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); - const weapon_type *dwtype = NULL; - const weapon_type *awtype = NULL; - const weapon *weapon; - - int rda, sk = 0, sd; - bool magic = false; - int da = dice_rand(damage); - - assert(du->number > 0); - ++at.fighter->hits; - - switch (type) { - case AT_STANDARD: - weapon = select_weapon(at, true, missile); - sk = weapon_effskill(at, dt, weapon, true, missile); - if (weapon) - awtype = weapon->type; - if (awtype && fval(awtype, WTF_MAGICAL)) - magic = true; - break; - case AT_NATURAL: - sk = weapon_effskill(at, dt, NULL, true, missile); - break; - case AT_SPELL: - case AT_COMBATSPELL: - magic = true; - break; - default: - break; - } - weapon = select_weapon(dt, false, true); /* missile=true to get the unmodified best weapon she has */ - sd = weapon_effskill(dt, at, weapon, false, false); - if (weapon != NULL) - dwtype = weapon->type; - - if (is_riding(at) && (awtype == NULL || (fval(awtype, WTF_HORSEBONUS) - && !fval(awtype, WTF_MISSILE)))) { - da += CavalryBonus(au, dt, BONUS_DAMAGE); - } - if (armor) { ar += armor->prot; if (armor->projectile > 0 && chance(armor->projectile)) { @@ -1182,6 +1130,90 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile) 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) +{ + item **pitm; + fighter *df = dt.fighter; + fighter *af = at.fighter; + unit *au = af->unit; + unit *du = df->unit; + battle *b = df->side->battle; + int heiltrank = 0; + + /* Schild */ + side *ds = df->side; + 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; + int da = dice_rand(damage); + + assert(du->number > 0); + ++at.fighter->hits; + + switch (type) { + case AT_STANDARD: + weapon = select_weapon(at, true, missile); + sk = weapon_effskill(at, dt, weapon, true, missile); + if (weapon) + awtype = weapon->type; + if (awtype && fval(awtype, WTF_MAGICAL)) + magic = true; + break; + case AT_NATURAL: + sk = weapon_effskill(at, dt, NULL, true, missile); + break; + case AT_SPELL: + case AT_COMBATSPELL: + magic = true; + break; + default: + break; + } + weapon = select_weapon(dt, false, true); /* missile=true to get the unmodified best weapon she has */ + sd = weapon_effskill(dt, at, weapon, false, false); + if (weapon != NULL) + dwtype = weapon->type; + + if (is_riding(at) && (awtype == NULL || (fval(awtype, WTF_HORSEBONUS) + && !fval(awtype, WTF_MISSILE)))) { + da += CavalryBonus(au, dt, BONUS_DAMAGE); + } + + ar = calculate_armor(dt, dwtype, awtype, magic ? &res : 0); + + if (magic) { + da = (int)(_max(da * res, 0)); + } + if (type != AT_COMBATSPELL && type != AT_SPELL) { if (damage_rules & DAMAGE_CRITICAL) { double kritchance = (sk * 3 - sd) / 200.0; @@ -1218,30 +1250,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..bb76d4d61 100644 --- a/src/battle.test.c +++ b/src/battle.test.c @@ -204,6 +204,31 @@ static void test_building_defence_bonus(CuTest * tc) test_cleanup(); } +static void test_calculate_armor(CuTest * tc) +{ + troop dt; + battle *b; + region *r; + unit *du; + side *ds; + fighter *df; + weapon_type *awtype = 0, *dwtype = 0; + int result; + + test_cleanup(); + r = test_create_region(0, 0, 0); + du = test_create_unit(test_create_faction(NULL), r); + + b = make_battle(r); + ds = make_side(b, du->faction, 0, 0, 0); + df = make_fighter(b, du, ds, false); + dt.fighter = df; + dt.index = 0; + result = calculate_armor(dt, dwtype, awtype, 0); + CuAssertIntEquals(tc, 0, result); + test_cleanup(); +} + CuSuite *get_battle_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -212,5 +237,6 @@ 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); return suite; } From cc89e9c9daa71399e38751fe911ec7c4207e2a4e Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 3 Nov 2015 17:33:44 +0100 Subject: [PATCH 3/6] fix crash when rules do not define a unicorn item --- src/magic.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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 */ From f959f43f1e693aa7a1c85f79a4e13c523a20a30e Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 3 Nov 2015 18:59:09 +0100 Subject: [PATCH 4/6] extensive tests for armor calculation --- src/battle.c | 7 +++-- src/battle.test.c | 72 +++++++++++++++++++++++++++++++++++++++-------- src/kernel/item.c | 2 +- 3 files changed, 66 insertions(+), 15 deletions(-) diff --git a/src/battle.c b/src/battle.c index cf8cf9b35..6d7b7fb79 100644 --- a/src/battle.c +++ b/src/battle.c @@ -1089,13 +1089,13 @@ int calculate_armor(troop dt, const weapon_type *dwtype, const weapon_type *awty if (armor) { ar += armor->prot; if (armor->projectile > 0 && chance(armor->projectile)) { - return false; + return -1; } } if (shield) { ar += shield->prot; if (shield->projectile > 0 && chance(shield->projectile)) { - return false; + return -1; } } @@ -1209,6 +1209,9 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile) } ar = calculate_armor(dt, dwtype, awtype, magic ? &res : 0); + if (ar < 0) { + return false; + } if (magic) { da = (int)(_max(da * res, 0)); diff --git a/src/battle.test.c b/src/battle.test.c index bb76d4d61..99defe3ec 100644 --- a/src/battle.test.c +++ b/src/battle.test.c @@ -204,28 +204,76 @@ 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; - side *ds; - fighter *df; - weapon_type *awtype = 0, *dwtype = 0; - int result; + 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); - du = test_create_unit(test_create_faction(NULL), r); - - b = make_battle(r); - ds = make_side(b, du->faction, 0, 0, 0); - df = make_fighter(b, du, ds, false); - dt.fighter = df; + 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"); dt.index = 0; - result = calculate_armor(dt, dwtype, awtype, 0); - CuAssertIntEquals(tc, 0, result); + du = test_create_unit(test_create_faction(rc), r); + + 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(); } 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) { From cfc31c65988bc0e3b22caa7f5341a37e6e2a33b8 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 3 Nov 2015 19:11:03 +0100 Subject: [PATCH 5/6] provide a failing test for issue 2149 --- src/battle.test.c | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/battle.test.c b/src/battle.test.c index 99defe3ec..8d8f83b8a 100644 --- a/src/battle.test.c +++ b/src/battle.test.c @@ -232,8 +232,8 @@ static void test_calculate_armor(CuTest * tc) 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"); - dt.index = 0; 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)); @@ -277,6 +277,44 @@ static void test_calculate_armor(CuTest * tc) 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(); @@ -286,5 +324,6 @@ CuSuite *get_battle_suite(void) 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; } From 154c2076a26e77cc0d85e4b1e6dd34e62692a060 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 3 Nov 2015 19:12:40 +0100 Subject: [PATCH 6/6] fixing bug 2149: projectile armor shall only protect against projectiles https://bugs.eressea.de/view.php?id=2149 --- src/battle.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/battle.c b/src/battle.c index 6d7b7fb79..853c908d7 100644 --- a/src/battle.c +++ b/src/battle.c @@ -1085,16 +1085,17 @@ int calculate_armor(troop dt, const weapon_type *dwtype, const weapon_type *awty 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 (armor->projectile > 0 && chance(armor->projectile)) { + if (missile && armor->projectile > 0 && chance(armor->projectile)) { return -1; } } if (shield) { ar += shield->prot; - if (shield->projectile > 0 && chance(shield->projectile)) { + if (missile && shield->projectile > 0 && chance(shield->projectile)) { return -1; } }