From aed7432bb2d918d61d429154e973f050bb9a98ca Mon Sep 17 00:00:00 2001 From: Steffen Mecke Date: Tue, 4 Sep 2018 16:41:46 +0200 Subject: [PATCH 1/5] fix magic resistance (bug 2480) --- scripts/tests/e2/spells.lua | 13 +++++++++++++ src/battle.c | 1 - 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/scripts/tests/e2/spells.lua b/scripts/tests/e2/spells.lua index a9f49d2b8..6a34d98db 100644 --- a/scripts/tests/e2/spells.lua +++ b/scripts/tests/e2/spells.lua @@ -136,3 +136,16 @@ function test_familiar_lynx() assert_equal(1, u:get_skill('magic')) assert_equal(1, u:get_skill('perception')) end + +function test_bug_2480() + local r = region.create(0, 0, "plain") + local f = faction.create("human", "hodor@eressea.de", "de") + local u1 = unit.create(f, r, 1) + local monster = unit.create(get_monsters(), r, 1, "wyrm") + u1.number = 30 + u1.hp = u1.hp_max * u1.number + monster:add_order("ATTACK " .. itoa36(u1.id)) + process_orders() + assert_equal(0, u1.number); + write_reports() +end diff --git a/src/battle.c b/src/battle.c index 9e2f841c0..64808747d 100644 --- a/src/battle.c +++ b/src/battle.c @@ -1169,7 +1169,6 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile) } if (magic) { - res = frac_sub(frac_one, res); res = frac_mul(frac_make(da, 1), res); da = res.sa[0] / res.sa[1]; } From c15b4c8fbfa61153cbbe278ebc622db48de9afab Mon Sep 17 00:00:00 2001 From: Steffen Mecke Date: Tue, 11 Sep 2018 17:17:47 +0200 Subject: [PATCH 2/5] refactor terminate (calculate_armor/resistance) --- scripts/tests/e2/e2features.lua | 1 + scripts/tests/e2/spells.lua | 3 +- src/battle.c | 156 +++++++++++++++++--------------- src/battle.h | 5 +- src/battle.test.c | 61 +++++++++---- 5 files changed, 130 insertions(+), 96 deletions(-) diff --git a/scripts/tests/e2/e2features.lua b/scripts/tests/e2/e2features.lua index bf90ab6a0..9db76dec9 100644 --- a/scripts/tests/e2/e2features.lua +++ b/scripts/tests/e2/e2features.lua @@ -519,6 +519,7 @@ function test_buy_sell() end function test_seaserpent_attack() + -- FIXME what does this test do? local r = region.create(0, 0, 'ocean') local sh = ship.create(r, 'boat') local us = unit.create(get_monsters(), r, 1, 'seaserpent') diff --git a/scripts/tests/e2/spells.lua b/scripts/tests/e2/spells.lua index 6a34d98db..5d718247a 100644 --- a/scripts/tests/e2/spells.lua +++ b/scripts/tests/e2/spells.lua @@ -139,7 +139,7 @@ end function test_bug_2480() local r = region.create(0, 0, "plain") - local f = faction.create("human", "hodor@eressea.de", "de") + local f = faction.create("human", "2480@eressea.de", "de") local u1 = unit.create(f, r, 1) local monster = unit.create(get_monsters(), r, 1, "wyrm") u1.number = 30 @@ -147,5 +147,4 @@ function test_bug_2480() monster:add_order("ATTACK " .. itoa36(u1.id)) process_orders() assert_equal(0, u1.number); - write_reports() end diff --git a/src/battle.c b/src/battle.c index 64808747d..11d804efb 100644 --- a/src/battle.c +++ b/src/battle.c @@ -555,8 +555,7 @@ static weapon *preferred_weapon(const troop t, bool attacking) return melee; } -static weapon *select_weapon(const troop t, bool attacking, - bool ismissile) +weapon *select_weapon(const troop t, bool attacking, bool ismissile) /* select the primary weapon for this trooper */ { if (attacking) { @@ -770,7 +769,7 @@ bool missile) return skill; } -static const armor_type *select_armor(troop t, bool shield) +const armor_type *select_armor(troop t, bool shield) { unsigned int type = shield ? ATF_SHIELD : 0; unit *u = t.fighter->unit; @@ -1012,85 +1011,87 @@ 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, variant *magres) { +int calculate_armor(troop dt, const weapon_type *dwtype, const weapon_type *awtype, + const armor_type *armor, const armor_type *shield, bool magic) { 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); + int total_armor = 0, nat_armor, magic_armor; bool missile = awtype && (awtype->flags&WTF_MISSILE); if (armor) { - ar += armor->prot; + total_armor += armor->prot; if (missile && armor->projectile > 0 && chance(armor->projectile)) { return -1; } } if (shield) { - ar += shield->prot; + total_armor += shield->prot; if (missile && shield->projectile > 0 && chance(shield->projectile)) { return -1; } } + if (magic) { + /* gegen Magie wirkt nur natuerliche und magische Ruestung */ + total_armor = 0; + } + /* nat�rliche R�stung */ - an = natural_armor(du); + nat_armor = natural_armor(du); /* magische R�stung durch Artefakte oder Spr�che */ /* Momentan nur Trollg�rtel und Werwolf-Eigenschaft */ - am = select_magicarmor(dt); + magic_armor = select_magicarmor(dt); if (rule_nat_armor == 0) { /* nat�rliche R�stung ist halbkumulativ */ - if (ar > 0) { - ar += an / 2; + if (total_armor > 0) { + total_armor += nat_armor / 2; } else { - ar = an; + total_armor = nat_armor; } } else { /* use the higher value, add half the other value */ - ar = (ar > an) ? (ar + an / 2) : (an + ar / 2); + total_armor = (total_armor > nat_armor) ? (total_armor + nat_armor / 2) : (nat_armor + total_armor / 2); } if (awtype && fval(awtype, WTF_ARMORPIERCING)) { /* crossbows */ - ar /= 2; + total_armor /= 2; } - ar += am; + total_armor += magic_armor; - if (magres) { - /* calculate damage multiplier for magical damage */ - variant res; + return total_armor; +} + +variant calculate_resistance(troop dt, const weapon_type *dwtype, const armor_type *armor, const armor_type *shield) { + fighter *df = dt.fighter; + unit *du = df->unit; + /* calculate damage multiplier for magical damage */ + variant res; - res = frac_sub(frac_one, magic_resistance(du)); + res = frac_sub(frac_one, magic_resistance(du)); - if (u_race(du)->battle_flags & BF_EQUIPMENT) { - /* der Effekt von Laen steigt nicht linear */ - if (armor && fval(armor, ATF_LAEN)) { - res = frac_mul(res, frac_sub(frac_one, armor->magres)); - } - if (shield && fval(shield, ATF_LAEN)) { - res = frac_mul(res, frac_sub(frac_one, shield->magres)); - } - if (dwtype) { - res = frac_mul(res, frac_sub(frac_one, dwtype->magres)); - } - } - - /* gegen Magie wirkt nur natuerliche und magische Ruestung */ - ar = an + am; - if (res.sa[0] >= 0) { - *magres = res; - } - else { - *magres = frac_make(0, 1); - } + if (u_race(du)->battle_flags & BF_EQUIPMENT) { + /* der Effekt von Laen steigt nicht linear */ + if (armor && fval(armor, ATF_LAEN)) { + res = frac_mul(res, frac_sub(frac_one, armor->magres)); } - - return ar; + if (shield && fval(shield, ATF_LAEN)) { + res = frac_mul(res, frac_sub(frac_one, shield->magres)); + } + if (dwtype) { + res = frac_mul(res, frac_sub(frac_one, dwtype->magres)); + } + } + if (res.sa[0] >= 0) { + return res; + } else { + return frac_zero; + } } static bool resurrect_troop(troop dt) @@ -1109,7 +1110,7 @@ static bool resurrect_troop(troop dt) } bool -terminate(troop dt, troop at, int type, const char *damage, bool missile) +terminate(troop dt, troop at, int type, const char *damage_formula, bool missile) { item **pitm; fighter *df = dt.fighter; @@ -1120,16 +1121,19 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile) /* Schild */ side *ds = df->side; - int ar; + int armor_value; const weapon_type *dwtype = NULL; const weapon_type *awtype = NULL; const weapon *weapon; - variant res = frac_one; + const armor_type *armor = NULL; + const armor_type *shield = NULL; - int rda, sk = 0, sd; + variant resistance_factor = frac_one; + + int reduced_damage, attskill = 0, defskill; bool magic = false; - int da = dice_rand(damage); + int damage = dice_rand(damage_formula); assert(du->number > 0); ++at.fighter->hits; @@ -1137,14 +1141,14 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile) switch (type) { case AT_STANDARD: weapon = select_weapon(at, true, missile); - sk = weapon_effskill(at, dt, weapon, true, missile); + attskill = 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); + attskill = weapon_effskill(at, dt, NULL, true, missile); break; case AT_SPELL: case AT_COMBATSPELL: @@ -1154,63 +1158,69 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile) 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); + defskill = 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); + damage += CavalryBonus(au, dt, BONUS_DAMAGE); } - ar = calculate_armor(dt, dwtype, awtype, magic ? &res : 0); - if (ar < 0) { + armor = select_armor(dt, false); + shield = select_armor(dt, true); + + armor_value = calculate_armor(dt, dwtype, awtype, armor, shield, magic); + if (magic) { + resistance_factor = calculate_resistance(dt, dwtype, armor, shield); + } + if (armor_value < 0) { return false; } if (magic) { - res = frac_mul(frac_make(da, 1), res); - da = res.sa[0] / res.sa[1]; + variant reduced_damage = frac_mul(frac_make(damage, 1), resistance_factor); + damage = reduced_damage.sa[0] / reduced_damage.sa[1]; } if (type != AT_COMBATSPELL && type != AT_SPELL) { if (rule_damage & DAMAGE_CRITICAL) { - double kritchance = (sk * 3 - sd) / 200.0; + double kritchance = (attskill * 3 - defskill) / 200.0; int maxk = 4; kritchance = fmax(kritchance, 0.005); kritchance = fmin(0.9, kritchance); while (maxk-- && chance(kritchance)) { - da += dice_rand(damage); + damage += dice_rand(damage_formula); } } - da += rc_specialdamage(au, du, awtype); + damage += rc_specialdamage(au, du, awtype); if (awtype != NULL && fval(awtype, WTF_MISSILE)) { /* missile weapon bonus */ if (rule_damage & DAMAGE_MISSILE_BONUS) { - da += af->person[at.index].damage_rear; + damage += af->person[at.index].damage_rear; } } else { /* melee bonus */ if (rule_damage & DAMAGE_MELEE_BONUS) { - da += af->person[at.index].damage; + damage += af->person[at.index].damage; } } /* Skilldifferenzbonus */ if (rule_damage & DAMAGE_SKILL_BONUS) { - da += MAX(0, (sk - sd) / DAMAGE_QUOTIENT); + damage += MAX(0, (attskill - defskill) / DAMAGE_QUOTIENT); } } - rda = MAX(da - ar, 0); + reduced_damage = MAX(damage - armor_value, 0); if ((u_race(du)->battle_flags & BF_INV_NONMAGIC) && !magic) - rda = 0; + reduced_damage = 0; else { int qi; selist *ql; @@ -1224,23 +1234,23 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile) i |= WTF_BLUNT; if (i && awtype && fval(awtype, i)) - rda /= 2; + reduced_damage /= 2; /* Schilde */ for (qi = 0, ql = b->meffects; ql; selist_advance(&ql, &qi, 1)) { meffect *me = (meffect *)selist_get(ql, qi); if (meffect_protection(b, me, ds) != 0) { - assert(0 <= rda); /* rda sollte hier immer mindestens 0 sein */ + assert(0 <= reduced_damage); /* rda sollte hier immer mindestens 0 sein */ /* jeder Schaden wird um effect% reduziert bis der Schild duration * Trefferpunkte aufgefangen hat */ if (me->typ == SHIELD_REDUCE) { - int hp = rda * (me->effect / 100); - rda -= hp; + int hp = reduced_damage * (me->effect / 100); + reduced_damage -= hp; me->duration -= hp; } /* gibt R�stung +effect f�r duration Treffer */ if (me->typ == SHIELD_ARMOR) { - rda = MAX(rda - me->effect, 0); + reduced_damage = MAX(reduced_damage - me->effect, 0); me->duration--; } } @@ -1248,10 +1258,10 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile) } assert(dt.index >= 0 && dt.index < du->number); - if (rda>0) { - df->person[dt.index].hp -= rda; + if (reduced_damage>0) { + df->person[dt.index].hp -= reduced_damage; if (u_race(au) == get_race(RC_DAEMON)) { - vampirism(at, rda); + vampirism(at, reduced_damage); } if (b->turn>1) { /* someone on the ship got damaged, damage the ship */ diff --git a/src/battle.h b/src/battle.h index 48fa7c63a..c747f49e5 100644 --- a/src/battle.h +++ b/src/battle.h @@ -233,7 +233,10 @@ extern "C" { int count_enemies(struct battle *b, const struct fighter *af, int minrow, int maxrow, int select); int natural_armor(struct unit * u); - int calculate_armor(troop dt, const struct weapon_type *dwtype, const struct weapon_type *awtype, union variant *magres); + const struct armor_type *select_armor(struct troop t, bool shield); + struct weapon *select_weapon(const struct troop t, bool attacking, bool ismissile); + int calculate_armor(troop dt, const struct weapon_type *dwtype, const struct weapon_type *awtype, const struct armor_type *armor, const struct armor_type *shield, bool magic); + union variant calculate_resistance(struct troop dt, const struct weapon_type *dwtype, const struct armor_type *armor, const struct armor_type *shield); bool terminate(troop dt, troop at, int type, const char *damage, bool missile); void message_all(battle * b, struct message *m); diff --git a/src/battle.test.c b/src/battle.test.c index 7c7012e77..3c35ef640 100644 --- a/src/battle.test.c +++ b/src/battle.test.c @@ -365,7 +365,10 @@ static void test_calculate_armor(CuTest * tc) dt.index = 0; dt.fighter = setup_fighter(&b, du); - CuAssertIntEquals_Msg(tc, "default ac", 0, calculate_armor(dt, 0, 0, &magres)); + CuAssertIntEquals_Msg(tc, "default ac", 0, calculate_armor(dt, 0, 0, select_armor(dt, false), select_armor(dt, true), false)); + magres = calculate_resistance(dt, + select_weapon(dt, false, true) ? select_weapon(dt, false, true)->type : 0, + select_armor(dt, false), select_armor(dt, true)); CuAssertIntEquals_Msg(tc, "magres unmodified", magres.sa[0], magres.sa[1]); free_battle(b); @@ -373,10 +376,10 @@ static void test_calculate_armor(CuTest * tc) i_change(&du->items, ibelt, 1); dt.fighter = setup_fighter(&b, du); CuAssertIntEquals_Msg(tc, "without natural armor", 0, natural_armor(du)); - CuAssertIntEquals_Msg(tc, "magical armor", 1, calculate_armor(dt, 0, 0, 0)); + CuAssertIntEquals_Msg(tc, "magical armor", 1, calculate_armor(dt, 0, 0, select_armor(dt, false), select_armor(dt, true), false)); rc->armor = 2; CuAssertIntEquals_Msg(tc, "with natural armor", 2, natural_armor(du)); - CuAssertIntEquals_Msg(tc, "natural armor", 3, calculate_armor(dt, 0, 0, 0)); + CuAssertIntEquals_Msg(tc, "natural armor", 3, calculate_armor(dt, 0, 0, select_armor(dt, false), select_armor(dt, true), false)); rc->armor = 0; free_battle(b); @@ -385,28 +388,34 @@ static void test_calculate_armor(CuTest * tc) i_change(&du->items, ichain, 1); dt.fighter = setup_fighter(&b, du); rc->battle_flags &= ~BF_EQUIPMENT; - CuAssertIntEquals_Msg(tc, "require BF_EQUIPMENT", 1, calculate_armor(dt, 0, 0, 0)); + CuAssertIntEquals_Msg(tc, "require BF_EQUIPMENT", 1, calculate_armor(dt, 0, 0, select_armor(dt, false), select_armor(dt, true), false)); free_battle(b); b = NULL; rc->battle_flags |= BF_EQUIPMENT; dt.fighter = setup_fighter(&b, du); - CuAssertIntEquals_Msg(tc, "stack equipment rc", 5, calculate_armor(dt, 0, 0, 0)); + CuAssertIntEquals_Msg(tc, "stack equipment rc", 5, calculate_armor(dt, 0, 0, select_armor(dt, false), select_armor(dt, true), false)); rc->armor = 2; - CuAssertIntEquals_Msg(tc, "natural armor adds 50%", 6, calculate_armor(dt, 0, 0, 0)); + CuAssertIntEquals_Msg(tc, "natural armor adds 50%", 6, calculate_armor(dt, 0, 0, select_armor(dt, false), select_armor(dt, true), false)); wtype->flags = WTF_NONE; - CuAssertIntEquals_Msg(tc, "regular weapon has no effect", 6, calculate_armor(dt, 0, wtype, 0)); + CuAssertIntEquals_Msg(tc, "regular weapon has no effect", 6, calculate_armor(dt, 0, wtype, select_armor(dt, false), select_armor(dt, true), false)); wtype->flags = WTF_ARMORPIERCING; - CuAssertIntEquals_Msg(tc, "armor piercing weapon", 3, calculate_armor(dt, 0, wtype, 0)); + CuAssertIntEquals_Msg(tc, "armor piercing weapon", 3, calculate_armor(dt, 0, wtype, select_armor(dt, false), select_armor(dt, true), false)); wtype->flags = WTF_NONE; - CuAssertIntEquals_Msg(tc, "magical attack", 3, calculate_armor(dt, 0, 0, &magres)); + CuAssertIntEquals_Msg(tc, "magical attack", 3, calculate_armor(dt, 0, 0, select_armor(dt, false), select_armor(dt, true), true)); + magres = calculate_resistance(dt, + select_weapon(dt, false, true) ? select_weapon(dt, false, true)->type : 0, + select_armor(dt, false), select_armor(dt, true)); CuAssertIntEquals_Msg(tc, "magres unmodified", magres.sa[1], magres.sa[0]); ashield->flags |= ATF_LAEN; achain->flags |= ATF_LAEN; - magres = frac_one; - CuAssertIntEquals_Msg(tc, "laen armor", 3, calculate_armor(dt, 0, 0, &magres)); + + CuAssertIntEquals_Msg(tc, "laen armor", 3, calculate_armor(dt, 0, 0, select_armor(dt, false), select_armor(dt, true), true)); + magres = calculate_resistance(dt, + select_weapon(dt, false, true) ? select_weapon(dt, false, true)->type : 0, + select_armor(dt, false), select_armor(dt, true)); CuAssertIntEquals_Msg(tc, "laen magres bonus", 4, magres.sa[1]); free_battle(b); test_teardown(); @@ -437,14 +446,18 @@ static void test_magic_resistance(CuTest *tc) i_change(&du->items, ishield, 1); dt.fighter = setup_fighter(&b, du); - calculate_armor(dt, 0, 0, &magres); + magres = calculate_resistance(dt, + select_weapon(dt, false, true) ? select_weapon(dt, false, true)->type : 0, + select_armor(dt, false), select_armor(dt, true)); CuAssertIntEquals_Msg(tc, "no magres reduction", magres.sa[1], magres.sa[0]); magres = magic_resistance(du); CuAssertIntEquals_Msg(tc, "no magres reduction", 0, magres.sa[0]); ashield->flags |= ATF_LAEN; ashield->magres = v10p; - calculate_armor(dt, 0, 0, &magres); + magres = calculate_resistance(dt, + select_weapon(dt, false, true) ? select_weapon(dt, false, true)->type : 0, + select_armor(dt, false), select_armor(dt, true)); CuAssert(tc, "laen reduction => 10%%", frac_equal(frac_make(9, 10), magres)); free_battle(b); @@ -455,7 +468,9 @@ static void test_magic_resistance(CuTest *tc) ashield->flags |= ATF_LAEN; ashield->magres = v10p; dt.fighter = setup_fighter(&b, du); - calculate_armor(dt, 0, 0, &magres); + magres = calculate_resistance(dt, + select_weapon(dt, false, true) ? select_weapon(dt, false, true)->type : 0, + select_armor(dt, false), select_armor(dt, true)); CuAssert(tc, "2x laen reduction => 81%%", frac_equal(frac_make(81, 100), magres)); free_battle(b); @@ -464,12 +479,16 @@ static void test_magic_resistance(CuTest *tc) i_change(&du->items, ichain, -1); set_level(du, SK_MAGIC, 2); dt.fighter = setup_fighter(&b, du); - calculate_armor(dt, 0, 0, &magres); + magres = calculate_resistance(dt, + select_weapon(dt, false, true) ? select_weapon(dt, false, true)->type : 0, + select_armor(dt, false), select_armor(dt, true)); CuAssert(tc, "skill reduction => 90%%", frac_equal(magres, frac_make(9, 10))); magres = magic_resistance(du); CuAssert(tc, "skill reduction", frac_equal(magres, v10p)); rc->magres = v50p; /* percentage, gets added to skill bonus */ - calculate_armor(dt, 0, 0, &magres); + magres = calculate_resistance(dt, + select_weapon(dt, false, true) ? select_weapon(dt, false, true)->type : 0, + select_armor(dt, false), select_armor(dt, true)); CuAssert(tc, "race reduction => 40%%", frac_equal(magres, frac_make(4, 10))); magres = magic_resistance(du); CuAssert(tc, "race bonus => 60%%", frac_equal(magres, frac_make(60, 100))); @@ -477,7 +496,9 @@ static void test_magic_resistance(CuTest *tc) rc->magres = frac_make(15, 10); /* 150% resistance should not cause negative damage multiplier */ magres = magic_resistance(du); CuAssert(tc, "magic resistance is never > 0.9", frac_equal(magres, frac_make(9, 10))); - calculate_armor(dt, 0, 0, &magres); + magres = calculate_resistance(dt, + select_weapon(dt, false, true) ? select_weapon(dt, false, true)->type : 0, + select_armor(dt, false), select_armor(dt, true)); CuAssert(tc, "damage reduction is never < 0.1", frac_equal(magres, frac_make(1, 10))); free_battle(b); @@ -513,12 +534,12 @@ static void test_projectile_armor(CuTest * tc) 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)); + CuAssertIntEquals_Msg(tc, "projectile armor", -1, calculate_armor(dt, 0, wtype, select_armor(dt, false), select_armor(dt, true), false)); achain->projectile = 0.0; ashield->projectile = 1.0; - CuAssertIntEquals_Msg(tc, "projectile shield", -1, calculate_armor(dt, 0, wtype, 0)); + CuAssertIntEquals_Msg(tc, "projectile shield", -1, calculate_armor(dt, 0, wtype, select_armor(dt, false), select_armor(dt, true), false)); wtype->flags = WTF_NONE; - CuAssertIntEquals_Msg(tc, "no projectiles", 4, calculate_armor(dt, 0, wtype, 0)); + CuAssertIntEquals_Msg(tc, "no projectiles", 4, calculate_armor(dt, 0, wtype, select_armor(dt, false), select_armor(dt, true), false)); free_battle(b); test_teardown(); } From d9810449533d6d1243d8e3440ce59fcf75eb60c5 Mon Sep 17 00:00:00 2001 From: Steffen Mecke Date: Wed, 12 Sep 2018 09:37:22 +0200 Subject: [PATCH 3/5] refactor calculate_armor --- src/battle.c | 46 ++++++++++++------------- src/battle.h | 2 +- src/battle.test.c | 88 ++++++++++++++++++++--------------------------- 3 files changed, 61 insertions(+), 75 deletions(-) diff --git a/src/battle.c b/src/battle.c index 11d804efb..661982c3e 100644 --- a/src/battle.c +++ b/src/battle.c @@ -1012,8 +1012,9 @@ static int rc_specialdamage(const unit *au, const unit *du, const struct weapon_ } int calculate_armor(troop dt, const weapon_type *dwtype, const weapon_type *awtype, - const armor_type *armor, const armor_type *shield, bool magic) { - fighter *df = dt.fighter; + const armor_type *armor, const armor_type *shield, bool magic) { + + const fighter *df = dt.fighter; unit *du = df->unit; int total_armor = 0, nat_armor, magic_armor; bool missile = awtype && (awtype->flags&WTF_MISSILE); @@ -1067,31 +1068,35 @@ int calculate_armor(troop dt, const weapon_type *dwtype, const weapon_type *awty return total_armor; } -variant calculate_resistance(troop dt, const weapon_type *dwtype, const armor_type *armor, const armor_type *shield) { - fighter *df = dt.fighter; +int apply_resistance(int damage, troop dt, const weapon_type *dwtype, const armor_type *armor, const armor_type *shield, bool magic) { + const fighter *df = dt.fighter; unit *du = df->unit; + + if (!magic) + return damage; + /* calculate damage multiplier for magical damage */ - variant res; - - res = frac_sub(frac_one, magic_resistance(du)); + variant resistance_factor = frac_sub(frac_one, magic_resistance(du)); if (u_race(du)->battle_flags & BF_EQUIPMENT) { /* der Effekt von Laen steigt nicht linear */ if (armor && fval(armor, ATF_LAEN)) { - res = frac_mul(res, frac_sub(frac_one, armor->magres)); + resistance_factor = frac_mul(resistance_factor, frac_sub(frac_one, armor->magres)); } if (shield && fval(shield, ATF_LAEN)) { - res = frac_mul(res, frac_sub(frac_one, shield->magres)); + resistance_factor = frac_mul(resistance_factor, frac_sub(frac_one, shield->magres)); } if (dwtype) { - res = frac_mul(res, frac_sub(frac_one, dwtype->magres)); + resistance_factor = frac_mul(resistance_factor, frac_sub(frac_one, dwtype->magres)); } } - if (res.sa[0] >= 0) { - return res; - } else { - return frac_zero; + if (resistance_factor.sa[0] <= 0) { + return 0; } + + variant reduced_damage = frac_mul(frac_make(damage, 1), resistance_factor); + return reduced_damage.sa[0] / reduced_damage.sa[1]; + } static bool resurrect_troop(troop dt) @@ -1129,10 +1134,9 @@ terminate(troop dt, troop at, int type, const char *damage_formula, bool missile const armor_type *armor = NULL; const armor_type *shield = NULL; - variant resistance_factor = frac_one; - - int reduced_damage, attskill = 0, defskill; + int reduced_damage, attskill = 0, defskill = 0; bool magic = false; + int damage = dice_rand(damage_formula); assert(du->number > 0); @@ -1171,17 +1175,11 @@ terminate(troop dt, troop at, int type, const char *damage_formula, bool missile shield = select_armor(dt, true); armor_value = calculate_armor(dt, dwtype, awtype, armor, shield, magic); - if (magic) { - resistance_factor = calculate_resistance(dt, dwtype, armor, shield); - } if (armor_value < 0) { return false; } - if (magic) { - variant reduced_damage = frac_mul(frac_make(damage, 1), resistance_factor); - damage = reduced_damage.sa[0] / reduced_damage.sa[1]; - } + damage = apply_resistance(damage, dt, dwtype, armor, shield, magic); if (type != AT_COMBATSPELL && type != AT_SPELL) { if (rule_damage & DAMAGE_CRITICAL) { diff --git a/src/battle.h b/src/battle.h index c747f49e5..a35a44a0f 100644 --- a/src/battle.h +++ b/src/battle.h @@ -236,7 +236,7 @@ extern "C" { const struct armor_type *select_armor(struct troop t, bool shield); struct weapon *select_weapon(const struct troop t, bool attacking, bool ismissile); int calculate_armor(troop dt, const struct weapon_type *dwtype, const struct weapon_type *awtype, const struct armor_type *armor, const struct armor_type *shield, bool magic); - union variant calculate_resistance(struct troop dt, const struct weapon_type *dwtype, const struct armor_type *armor, const struct armor_type *shield); + int apply_resistance(int damage, struct troop dt, const struct weapon_type *dwtype, const struct armor_type *armor, const struct armor_type *shield, bool magic); bool terminate(troop dt, troop at, int type, const char *damage, bool missile); void message_all(battle * b, struct message *m); diff --git a/src/battle.test.c b/src/battle.test.c index 3c35ef640..ba0807472 100644 --- a/src/battle.test.c +++ b/src/battle.test.c @@ -339,6 +339,16 @@ static void test_natural_armor(CuTest * tc) test_teardown(); } +static int test_armor(troop dt, weapon_type *awtype, bool magic) { + return calculate_armor(dt, 0, awtype, select_armor(dt, false), select_armor(dt, true), magic); +} + +static int test_resistance(troop dt) { + return apply_resistance(1000, dt, + select_weapon(dt, false, true) ? select_weapon(dt, false, true)->type : 0, + select_armor(dt, false), select_armor(dt, true), true); +} + static void test_calculate_armor(CuTest * tc) { troop dt; @@ -349,7 +359,6 @@ static void test_calculate_armor(CuTest * tc) armor_type *ashield, *achain; item_type *ibelt, *ishield, *ichain; race *rc; - variant magres = frac_zero; variant v50p = frac_make(1, 2); test_setup(); @@ -365,21 +374,19 @@ static void test_calculate_armor(CuTest * tc) dt.index = 0; dt.fighter = setup_fighter(&b, du); - CuAssertIntEquals_Msg(tc, "default ac", 0, calculate_armor(dt, 0, 0, select_armor(dt, false), select_armor(dt, true), false)); - magres = calculate_resistance(dt, - select_weapon(dt, false, true) ? select_weapon(dt, false, true)->type : 0, - select_armor(dt, false), select_armor(dt, true)); - CuAssertIntEquals_Msg(tc, "magres unmodified", magres.sa[0], magres.sa[1]); + CuAssertIntEquals_Msg(tc, "default ac", 0, test_armor(dt, 0, false)); + + CuAssertIntEquals_Msg(tc, "magres unmodified", 1000, test_resistance(dt)); free_battle(b); b = NULL; i_change(&du->items, ibelt, 1); dt.fighter = setup_fighter(&b, du); CuAssertIntEquals_Msg(tc, "without natural armor", 0, natural_armor(du)); - CuAssertIntEquals_Msg(tc, "magical armor", 1, calculate_armor(dt, 0, 0, select_armor(dt, false), select_armor(dt, true), false)); + CuAssertIntEquals_Msg(tc, "magical armor", 1, test_armor(dt, 0, false)); rc->armor = 2; CuAssertIntEquals_Msg(tc, "with natural armor", 2, natural_armor(du)); - CuAssertIntEquals_Msg(tc, "natural armor", 3, calculate_armor(dt, 0, 0, select_armor(dt, false), select_armor(dt, true), false)); + CuAssertIntEquals_Msg(tc, "natural armor", 3, test_armor(dt, 0, false)); rc->armor = 0; free_battle(b); @@ -388,35 +395,30 @@ static void test_calculate_armor(CuTest * tc) i_change(&du->items, ichain, 1); dt.fighter = setup_fighter(&b, du); rc->battle_flags &= ~BF_EQUIPMENT; - CuAssertIntEquals_Msg(tc, "require BF_EQUIPMENT", 1, calculate_armor(dt, 0, 0, select_armor(dt, false), select_armor(dt, true), false)); + CuAssertIntEquals_Msg(tc, "require BF_EQUIPMENT", 1, test_armor(dt, 0, false)); free_battle(b); b = NULL; rc->battle_flags |= BF_EQUIPMENT; dt.fighter = setup_fighter(&b, du); - CuAssertIntEquals_Msg(tc, "stack equipment rc", 5, calculate_armor(dt, 0, 0, select_armor(dt, false), select_armor(dt, true), false)); + CuAssertIntEquals_Msg(tc, "stack equipment rc", 5, test_armor(dt, 0, false)); rc->armor = 2; - CuAssertIntEquals_Msg(tc, "natural armor adds 50%", 6, calculate_armor(dt, 0, 0, select_armor(dt, false), select_armor(dt, true), false)); + CuAssertIntEquals_Msg(tc, "natural armor adds 50%", 6, test_armor(dt, 0, false)); wtype->flags = WTF_NONE; - CuAssertIntEquals_Msg(tc, "regular weapon has no effect", 6, calculate_armor(dt, 0, wtype, select_armor(dt, false), select_armor(dt, true), false)); + CuAssertIntEquals_Msg(tc, "regular weapon has no effect", 6, test_armor(dt, wtype, false)); wtype->flags = WTF_ARMORPIERCING; - CuAssertIntEquals_Msg(tc, "armor piercing weapon", 3, calculate_armor(dt, 0, wtype, select_armor(dt, false), select_armor(dt, true), false)); + CuAssertIntEquals_Msg(tc, "armor piercing weapon", 3, test_armor(dt, wtype, false)); wtype->flags = WTF_NONE; - CuAssertIntEquals_Msg(tc, "magical attack", 3, calculate_armor(dt, 0, 0, select_armor(dt, false), select_armor(dt, true), true)); - magres = calculate_resistance(dt, - select_weapon(dt, false, true) ? select_weapon(dt, false, true)->type : 0, - select_armor(dt, false), select_armor(dt, true)); - CuAssertIntEquals_Msg(tc, "magres unmodified", magres.sa[1], magres.sa[0]); + CuAssertIntEquals_Msg(tc, "magical attack", 3, test_armor(dt, wtype, true)); + CuAssertIntEquals_Msg(tc, "magres unmodified", 1000, + test_resistance(dt)); ashield->flags |= ATF_LAEN; achain->flags |= ATF_LAEN; - CuAssertIntEquals_Msg(tc, "laen armor", 3, calculate_armor(dt, 0, 0, select_armor(dt, false), select_armor(dt, true), true)); - magres = calculate_resistance(dt, - select_weapon(dt, false, true) ? select_weapon(dt, false, true)->type : 0, - select_armor(dt, false), select_armor(dt, true)); - CuAssertIntEquals_Msg(tc, "laen magres bonus", 4, magres.sa[1]); + CuAssertIntEquals_Msg(tc, "laen armor", 3, test_armor(dt, wtype, true)); + CuAssertIntEquals_Msg(tc, "laen magres bonus", 250, test_resistance(dt)); free_battle(b); test_teardown(); } @@ -446,19 +448,17 @@ static void test_magic_resistance(CuTest *tc) i_change(&du->items, ishield, 1); dt.fighter = setup_fighter(&b, du); - magres = calculate_resistance(dt, - select_weapon(dt, false, true) ? select_weapon(dt, false, true)->type : 0, - select_armor(dt, false), select_armor(dt, true)); - CuAssertIntEquals_Msg(tc, "no magres reduction", magres.sa[1], magres.sa[0]); + CuAssertIntEquals_Msg(tc, "no magres reduction", 1000, test_resistance(dt)); magres = magic_resistance(du); CuAssertIntEquals_Msg(tc, "no magres reduction", 0, magres.sa[0]); ashield->flags |= ATF_LAEN; ashield->magres = v10p; - magres = calculate_resistance(dt, - select_weapon(dt, false, true) ? select_weapon(dt, false, true)->type : 0, - select_armor(dt, false), select_armor(dt, true)); - CuAssert(tc, "laen reduction => 10%%", frac_equal(frac_make(9, 10), magres)); + CuAssertIntEquals_Msg(tc, "laen reduction => 10%%", 900, test_resistance(dt)); + CuAssertIntEquals_Msg(tc, "no magic, no resistance", 1000, + apply_resistance(1000, dt, + select_weapon(dt, false, true) ? select_weapon(dt, false, true)->type : 0, + select_armor(dt, false), select_armor(dt, true), false)); free_battle(b); b = NULL; @@ -468,10 +468,7 @@ static void test_magic_resistance(CuTest *tc) ashield->flags |= ATF_LAEN; ashield->magres = v10p; dt.fighter = setup_fighter(&b, du); - magres = calculate_resistance(dt, - select_weapon(dt, false, true) ? select_weapon(dt, false, true)->type : 0, - select_armor(dt, false), select_armor(dt, true)); - CuAssert(tc, "2x laen reduction => 81%%", frac_equal(frac_make(81, 100), magres)); + CuAssertIntEquals_Msg(tc, "2x laen reduction => 81%%", 810, test_resistance(dt)); free_battle(b); b = NULL; @@ -479,27 +476,18 @@ static void test_magic_resistance(CuTest *tc) i_change(&du->items, ichain, -1); set_level(du, SK_MAGIC, 2); dt.fighter = setup_fighter(&b, du); - magres = calculate_resistance(dt, - select_weapon(dt, false, true) ? select_weapon(dt, false, true)->type : 0, - select_armor(dt, false), select_armor(dt, true)); - CuAssert(tc, "skill reduction => 90%%", frac_equal(magres, frac_make(9, 10))); + CuAssertIntEquals_Msg(tc, "skill reduction => 90%%", 900, test_resistance(dt)); magres = magic_resistance(du); CuAssert(tc, "skill reduction", frac_equal(magres, v10p)); rc->magres = v50p; /* percentage, gets added to skill bonus */ - magres = calculate_resistance(dt, - select_weapon(dt, false, true) ? select_weapon(dt, false, true)->type : 0, - select_armor(dt, false), select_armor(dt, true)); - CuAssert(tc, "race reduction => 40%%", frac_equal(magres, frac_make(4, 10))); + CuAssertIntEquals_Msg(tc, "race reduction => 40%%", 400, test_resistance(dt)); magres = magic_resistance(du); CuAssert(tc, "race bonus => 60%%", frac_equal(magres, frac_make(60, 100))); rc->magres = frac_make(15, 10); /* 150% resistance should not cause negative damage multiplier */ magres = magic_resistance(du); CuAssert(tc, "magic resistance is never > 0.9", frac_equal(magres, frac_make(9, 10))); - magres = calculate_resistance(dt, - select_weapon(dt, false, true) ? select_weapon(dt, false, true)->type : 0, - select_armor(dt, false), select_armor(dt, true)); - CuAssert(tc, "damage reduction is never < 0.1", frac_equal(magres, frac_make(1, 10))); + CuAssertIntEquals_Msg(tc, "damage reduction is never < 0.1", 100, test_resistance(dt)); free_battle(b); test_teardown(); @@ -534,12 +522,12 @@ static void test_projectile_armor(CuTest * tc) 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, select_armor(dt, false), select_armor(dt, true), false)); + CuAssertIntEquals_Msg(tc, "projectile armor", -1, test_armor(dt, wtype, false)); achain->projectile = 0.0; ashield->projectile = 1.0; - CuAssertIntEquals_Msg(tc, "projectile shield", -1, calculate_armor(dt, 0, wtype, select_armor(dt, false), select_armor(dt, true), false)); + CuAssertIntEquals_Msg(tc, "projectile shield", -1, test_armor(dt, wtype, false)); wtype->flags = WTF_NONE; - CuAssertIntEquals_Msg(tc, "no projectiles", 4, calculate_armor(dt, 0, wtype, select_armor(dt, false), select_armor(dt, true), false)); + CuAssertIntEquals_Msg(tc, "no projectiles", 4, test_armor(dt, wtype, false)); free_battle(b); test_teardown(); } From 69702df20328c7f0ff8331cf6b720763e6526f02 Mon Sep 17 00:00:00 2001 From: Steffen Mecke Date: Wed, 12 Sep 2018 09:40:34 +0200 Subject: [PATCH 4/5] refactor terminate finished --- src/battle.c | 319 +++++++++++++++++++++++++++------------------- src/battle.h | 1 - src/battle.test.c | 33 +++++ 3 files changed, 224 insertions(+), 129 deletions(-) diff --git a/src/battle.c b/src/battle.c index 661982c3e..c34daeb38 100644 --- a/src/battle.c +++ b/src/battle.c @@ -122,7 +122,7 @@ const troop no_troop = { 0, 0 }; #define DAMAGE_CRITICAL (1<<0) #define DAMAGE_MELEE_BONUS (1<<1) -#define DAMAGE_MISSILE_BONUS (1<<2) +#define DAMAGE_MISSILE_BONUS (1<<2) /* deprecated */ #define DAMAGE_SKILL_BONUS (1<<4) static int max_turns; @@ -169,7 +169,7 @@ static void init_rules(void) if (config_get_int("rules.combat.melee_bonus", 1)) { rule_damage |= DAMAGE_MELEE_BONUS; } - if (config_get_int("rules.combat.missile_bonus", 1)) { + if (config_get_int("rules.combat.missile_bonus", 1)) { /* deprecated */ rule_damage |= DAMAGE_MISSILE_BONUS; } if (config_get_int("rules.combat.skill_bonus", 1)) { @@ -949,6 +949,9 @@ void drain_exp(struct unit *u, int n) static void vampirism(troop at, int damage) { + const unit *au = at.fighter->unit; + + if (u_race(au) == get_race(RC_DAEMON)) { if (rule_vampire > 0) { int gain = damage / rule_vampire; int chance = damage - rule_vampire * gain; @@ -962,6 +965,16 @@ static void vampirism(troop at, int damage) at.fighter->person[at.index].hp = maxhp; } } + } +} + +static void ship_damage(int turn, unit *du) { + if (turn>1) { + /* someone on the ship got damaged, damage the ship */ + ship *sh = du->ship ? du->ship : leftship(du); + if (sh) + fset(sh, SF_DAMAGED); + } } #define MAXRACES 128 @@ -1114,23 +1127,181 @@ static bool resurrect_troop(troop dt) return false; } +static void demon_dazzle(fighter *af, troop dt) { + const fighter *df = dt.fighter; + if (u_race(af->unit) == get_race(RC_DAEMON)) { + if (!(df->person[dt.index].flags & (FL_COURAGE | FL_DAZZLED))) { + df->person[dt.index].flags |= FL_DAZZLED; + df->person[dt.index].defence--; + } + } +} + +static bool survives(fighter *af, troop dt, battle *b) { + const unit *du = af->unit; + const fighter *df = dt.fighter; + + if (df->person[dt.index].hp > 0) { /* Hat �berlebt */ + demon_dazzle(af, dt); + + return true; + } + + /* Sieben Leben */ + if (u_race(du) == get_race(RC_CAT) && (chance(1.0 / 7))) { + df->person[dt.index].hp = unit_max_hp(du); + return true; + } + + /* healing potions can avert a killing blow */ + if (resurrect_troop(dt)) { + message *m = msg_message("potionsave", "unit", du); + battle_message_faction(b, du->faction, m); + msg_release(m); + return true; + } + + return false; +} + +static void destroy_items(troop dt) { + unit *du = dt.fighter->unit; + + item **pitm; + + for (pitm = &du->items; *pitm;) { + item *itm = *pitm; + const item_type *itype = itm->type; + if (!(itype->flags & ITF_CURSED) && dt.index < itm->number) { + /* 25% Grundchance, das ein Item kaputtgeht. */ + if (rng_int() % 4 < 1) { + i_change(pitm, itype, -1); + } + } + if (*pitm == itm) { + pitm = &itm->next; + } + } + +} + +static void calculate_defence_type(troop dt, troop at, int type, bool missile, + const weapon_type **dwtype, int *defskill) { + const weapon *weapon; + weapon = select_weapon(dt, false, true); /* missile=true to get the unmodified best weapon she has */ + *defskill = weapon_effskill(dt, at, weapon, false, false); + if (weapon != NULL) + *dwtype = weapon->type; +} + +static void calculate_attack_type(troop dt, troop at, int type, bool missile, + const weapon_type **awtype, int *attskill, bool *magic) { + const weapon *weapon; + + switch (type) { + case AT_STANDARD: + weapon = select_weapon(at, true, missile); + *attskill = weapon_effskill(at, dt, weapon, true, missile); + if (weapon) + *awtype = weapon->type; + if (*awtype && fval(*awtype, WTF_MAGICAL)) + *magic = true; + break; + case AT_NATURAL: + *attskill = weapon_effskill(at, dt, NULL, true, missile); + break; + case AT_SPELL: + case AT_COMBATSPELL: + *magic = true; + break; + default: + break; + } +} + +static int crit_damage(int attskill, int defskill, const char *damage_formula) { + int damage = 0; + if (rule_damage & DAMAGE_CRITICAL) { + double kritchance = (attskill * 3 - defskill) / 200.0; + int maxk = 4; + + kritchance = fmax(kritchance, 0.005); + kritchance = fmin(0.9, kritchance); + + while (maxk-- && chance(kritchance)) { + damage += dice_rand(damage_formula); + } + } + return damage; +} + +static int apply_race_resistance(int reduced_damage, fighter *df, + const weapon_type *awtype, bool magic) { + unit *du = df->unit; + + if ((u_race(du)->battle_flags & BF_INV_NONMAGIC) && !magic) + reduced_damage = 0; + else { + unsigned int i = 0; + + if (u_race(du)->battle_flags & BF_RES_PIERCE) + i |= WTF_PIERCE; + if (u_race(du)->battle_flags & BF_RES_CUT) + i |= WTF_CUT; + if (u_race(du)->battle_flags & BF_RES_BASH) + i |= WTF_BLUNT; + + if (i && awtype && fval(awtype, i)) + reduced_damage /= 2; + } + return reduced_damage; +} + +static int apply_magicshield(int reduced_damage, fighter *df, + const weapon_type *awtype, battle *b, bool magic) { + side *ds = df->side; + selist *ql; + int qi; + + if (reduced_damage <= 0) + return 0; + + /* Schilde */ + for (qi = 0, ql = b->meffects; ql; selist_advance(&ql, &qi, 1)) { + meffect *me = (meffect *) selist_get(ql, qi); + if (meffect_protection(b, me, ds) != 0) { + assert(0 <= reduced_damage); /* rda sollte hier immer mindestens 0 sein */ + /* jeder Schaden wird um effect% reduziert bis der Schild duration + * Trefferpunkte aufgefangen hat */ + if (me->typ == SHIELD_REDUCE) { + int hp = reduced_damage * (me->effect / 100); + reduced_damage -= hp; + me->duration -= hp; + } + /* gibt R�stung +effect f�r duration Treffer */ + if (me->typ == SHIELD_ARMOR) { + reduced_damage = MAX(reduced_damage - me->effect, 0); + me->duration--; + } + } + } + + return reduced_damage; +} + bool terminate(troop dt, troop at, int type, const char *damage_formula, 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; - /* Schild */ - side *ds = df->side; int armor_value; const weapon_type *dwtype = NULL; const weapon_type *awtype = NULL; - const weapon *weapon; const armor_type *armor = NULL; const armor_type *shield = NULL; @@ -1142,29 +1313,8 @@ terminate(troop dt, troop at, int type, const char *damage_formula, bool missile assert(du->number > 0); ++at.fighter->hits; - switch (type) { - case AT_STANDARD: - weapon = select_weapon(at, true, missile); - attskill = weapon_effskill(at, dt, weapon, true, missile); - if (weapon) - awtype = weapon->type; - if (awtype && fval(awtype, WTF_MAGICAL)) - magic = true; - break; - case AT_NATURAL: - attskill = 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 */ - defskill = weapon_effskill(dt, at, weapon, false, false); - if (weapon != NULL) - dwtype = weapon->type; + calculate_attack_type(at, dt, type, missile, &awtype, &attskill, &magic); + calculate_defence_type(at, dt, type, missile, &awtype, &attskill); if (is_riding(at) && (awtype == NULL || (fval(awtype, WTF_HORSEBONUS) && !fval(awtype, WTF_MISSILE)))) { @@ -1182,27 +1332,11 @@ terminate(troop dt, troop at, int type, const char *damage_formula, bool missile damage = apply_resistance(damage, dt, dwtype, armor, shield, magic); if (type != AT_COMBATSPELL && type != AT_SPELL) { - if (rule_damage & DAMAGE_CRITICAL) { - double kritchance = (attskill * 3 - defskill) / 200.0; - int maxk = 4; - - kritchance = fmax(kritchance, 0.005); - kritchance = fmin(0.9, kritchance); - - while (maxk-- && chance(kritchance)) { - damage += dice_rand(damage_formula); - } - } + damage += crit_damage(attskill, defskill, damage_formula); damage += rc_specialdamage(au, du, awtype); - if (awtype != NULL && fval(awtype, WTF_MISSILE)) { - /* missile weapon bonus */ - if (rule_damage & DAMAGE_MISSILE_BONUS) { - damage += af->person[at.index].damage_rear; - } - } - else { + if (awtype == NULL || !fval(awtype, WTF_MISSILE)) { /* melee bonus */ if (rule_damage & DAMAGE_MELEE_BONUS) { damage += af->person[at.index].damage; @@ -1217,96 +1351,25 @@ terminate(troop dt, troop at, int type, const char *damage_formula, bool missile reduced_damage = MAX(damage - armor_value, 0); - if ((u_race(du)->battle_flags & BF_INV_NONMAGIC) && !magic) - reduced_damage = 0; - else { - int qi; - selist *ql; - unsigned int i = 0; - - if (u_race(du)->battle_flags & BF_RES_PIERCE) - i |= WTF_PIERCE; - if (u_race(du)->battle_flags & BF_RES_CUT) - i |= WTF_CUT; - if (u_race(du)->battle_flags & BF_RES_BASH) - i |= WTF_BLUNT; - - if (i && awtype && fval(awtype, i)) - reduced_damage /= 2; - - /* Schilde */ - for (qi = 0, ql = b->meffects; ql; selist_advance(&ql, &qi, 1)) { - meffect *me = (meffect *)selist_get(ql, qi); - if (meffect_protection(b, me, ds) != 0) { - assert(0 <= reduced_damage); /* rda sollte hier immer mindestens 0 sein */ - /* jeder Schaden wird um effect% reduziert bis der Schild duration - * Trefferpunkte aufgefangen hat */ - if (me->typ == SHIELD_REDUCE) { - int hp = reduced_damage * (me->effect / 100); - reduced_damage -= hp; - me->duration -= hp; - } - /* gibt R�stung +effect f�r duration Treffer */ - if (me->typ == SHIELD_ARMOR) { - reduced_damage = MAX(reduced_damage - me->effect, 0); - me->duration--; - } - } - } - } + reduced_damage = apply_race_resistance(reduced_damage, df, awtype, magic); + reduced_damage = apply_magicshield(reduced_damage, df, awtype, b, magic); assert(dt.index >= 0 && dt.index < du->number); - if (reduced_damage>0) { + if (reduced_damage > 0) { df->person[dt.index].hp -= reduced_damage; - if (u_race(au) == get_race(RC_DAEMON)) { - vampirism(at, reduced_damage); - } - if (b->turn>1) { - /* someone on the ship got damaged, damage the ship */ - ship *sh = du->ship ? du->ship : leftship(du); - if (sh) - fset(sh, SF_DAMAGED); - } - } - if (df->person[dt.index].hp > 0) { /* Hat �berlebt */ - if (u_race(au) == get_race(RC_DAEMON)) { - if (!(df->person[dt.index].flags & (FL_COURAGE | FL_DAZZLED))) { - df->person[dt.index].flags |= FL_DAZZLED; - df->person[dt.index].defence--; - } - } - return false; + vampirism(at, reduced_damage); + + ship_damage(b->turn, du); } - /* Sieben Leben */ - if (u_race(du) == get_race(RC_CAT) && (chance(1.0 / 7))) { - df->person[dt.index].hp = unit_max_hp(du); - return false; - } + if (survives(af, dt, b)) + return false; - /* healing potions can avert a killing blow */ - if (resurrect_troop(dt)) { - message *m = msg_message("potionsave", "unit", du); - battle_message_faction(b, du->faction, m); - msg_release(m); - return false; - } ++at.fighter->kills; - for (pitm = &du->items; *pitm;) { - item *itm = *pitm; - const item_type *itype = itm->type; - if (!(itype->flags & ITF_CURSED) && dt.index < itm->number) { - /* 25% Grundchance, das ein Item kaputtgeht. */ - if (rng_int() % 4 < 1) { - i_change(pitm, itype, -1); - } - } - if (*pitm == itm) { - pitm = &itm->next; - } - } + destroy_items(dt); + kill_troop(dt); return true; diff --git a/src/battle.h b/src/battle.h index a35a44a0f..bad2dd508 100644 --- a/src/battle.h +++ b/src/battle.h @@ -175,7 +175,6 @@ extern "C" { int attack; int defence; int damage; - int damage_rear; int flags; int speed; int reload; diff --git a/src/battle.test.c b/src/battle.test.c index ba0807472..a734f5428 100644 --- a/src/battle.test.c +++ b/src/battle.test.c @@ -565,6 +565,38 @@ static void test_battle_skilldiff(CuTest *tc) test_teardown(); } +static void test_terminate(CuTest * tc) +{ + troop at, dt; + battle *b = NULL; + region *r; + unit *au, *du; + race *rc; + + test_setup(); + r = test_create_region(0, 0, NULL); + + rc = test_create_race("human"); + au = test_create_unit(test_create_faction(rc), r); + du = test_create_unit(test_create_faction(rc), r); + dt.index = 0; + at.index = 0; + + at.fighter = setup_fighter(&b, au); + dt.fighter = setup_fighter(&b, du); + + CuAssertIntEquals_Msg(tc, "not killed", 0, terminate(dt, at, AT_STANDARD, "1d1", false)); + b = NULL; + at.fighter = setup_fighter(&b, au); + dt.fighter = setup_fighter(&b, du); + CuAssertIntEquals_Msg(tc, "killed", 1, terminate(dt, at, AT_STANDARD, "100d1", false)); + CuAssertIntEquals_Msg(tc, "number", 0, dt.fighter->person[0].hp); + + free_battle(b); + test_teardown(); +} + + static void test_battle_report_one(CuTest *tc) { battle * b = NULL; @@ -812,6 +844,7 @@ CuSuite *get_battle_suite(void) SUITE_ADD_TEST(suite, test_magic_resistance); SUITE_ADD_TEST(suite, test_projectile_armor); SUITE_ADD_TEST(suite, test_tactics_chance); + SUITE_ADD_TEST(suite, test_terminate); DISABLE_TEST(suite, test_drain_exp); return suite; } From a90ae46d5216c65561e1ed788757dd957f46a714 Mon Sep 17 00:00:00 2001 From: Steffen Mecke Date: Thu, 13 Sep 2018 10:59:15 +0200 Subject: [PATCH 5/5] assert assumption on armor --- src/battle.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/battle.c b/src/battle.c index c34daeb38..e609a1609 100644 --- a/src/battle.c +++ b/src/battle.c @@ -1078,6 +1078,8 @@ int calculate_armor(troop dt, const weapon_type *dwtype, const weapon_type *awty total_armor += magic_armor; + assert(total_armor >= 0 || !"armor < 0 means hit denied"); + return total_armor; }