refactor terminate finished

This commit is contained in:
Steffen Mecke 2018-09-12 09:40:34 +02:00
parent d981044953
commit 69702df203
3 changed files with 224 additions and 129 deletions

View File

@ -122,7 +122,7 @@ const troop no_troop = { 0, 0 };
#define DAMAGE_CRITICAL (1<<0) #define DAMAGE_CRITICAL (1<<0)
#define DAMAGE_MELEE_BONUS (1<<1) #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) #define DAMAGE_SKILL_BONUS (1<<4)
static int max_turns; static int max_turns;
@ -169,7 +169,7 @@ static void init_rules(void)
if (config_get_int("rules.combat.melee_bonus", 1)) { if (config_get_int("rules.combat.melee_bonus", 1)) {
rule_damage |= DAMAGE_MELEE_BONUS; 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; rule_damage |= DAMAGE_MISSILE_BONUS;
} }
if (config_get_int("rules.combat.skill_bonus", 1)) { 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) 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) { if (rule_vampire > 0) {
int gain = damage / rule_vampire; int gain = damage / rule_vampire;
int chance = damage - rule_vampire * gain; int chance = damage - rule_vampire * gain;
@ -962,6 +965,16 @@ static void vampirism(troop at, int damage)
at.fighter->person[at.index].hp = maxhp; 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 #define MAXRACES 128
@ -1114,23 +1127,181 @@ static bool resurrect_troop(troop dt)
return false; 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 <20>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 bool
terminate(troop dt, troop at, int type, const char *damage_formula, bool missile) terminate(troop dt, troop at, int type, const char *damage_formula, bool missile)
{ {
item **pitm;
fighter *df = dt.fighter; fighter *df = dt.fighter;
fighter *af = at.fighter; fighter *af = at.fighter;
unit *au = af->unit; unit *au = af->unit;
unit *du = df->unit; unit *du = df->unit;
battle *b = df->side->battle; battle *b = df->side->battle;
/* Schild */
side *ds = df->side;
int armor_value; int armor_value;
const weapon_type *dwtype = NULL; const weapon_type *dwtype = NULL;
const weapon_type *awtype = NULL; const weapon_type *awtype = NULL;
const weapon *weapon;
const armor_type *armor = NULL; const armor_type *armor = NULL;
const armor_type *shield = 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); assert(du->number > 0);
++at.fighter->hits; ++at.fighter->hits;
switch (type) { calculate_attack_type(at, dt, type, missile, &awtype, &attskill, &magic);
case AT_STANDARD: calculate_defence_type(at, dt, type, missile, &awtype, &attskill);
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;
if (is_riding(at) && (awtype == NULL || (fval(awtype, WTF_HORSEBONUS) if (is_riding(at) && (awtype == NULL || (fval(awtype, WTF_HORSEBONUS)
&& !fval(awtype, WTF_MISSILE)))) { && !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); damage = apply_resistance(damage, dt, dwtype, armor, shield, magic);
if (type != AT_COMBATSPELL && type != AT_SPELL) { if (type != AT_COMBATSPELL && type != AT_SPELL) {
if (rule_damage & DAMAGE_CRITICAL) { damage += crit_damage(attskill, defskill, damage_formula);
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 += rc_specialdamage(au, du, awtype); damage += rc_specialdamage(au, du, awtype);
if (awtype != NULL && fval(awtype, WTF_MISSILE)) { if (awtype == NULL || !fval(awtype, WTF_MISSILE)) {
/* missile weapon bonus */
if (rule_damage & DAMAGE_MISSILE_BONUS) {
damage += af->person[at.index].damage_rear;
}
}
else {
/* melee bonus */ /* melee bonus */
if (rule_damage & DAMAGE_MELEE_BONUS) { if (rule_damage & DAMAGE_MELEE_BONUS) {
damage += af->person[at.index].damage; 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); reduced_damage = MAX(damage - armor_value, 0);
if ((u_race(du)->battle_flags & BF_INV_NONMAGIC) && !magic) reduced_damage = apply_race_resistance(reduced_damage, df, awtype, magic);
reduced_damage = 0; reduced_damage = apply_magicshield(reduced_damage, df, awtype, b, magic);
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--;
}
}
}
}
assert(dt.index >= 0 && dt.index < du->number); assert(dt.index >= 0 && dt.index < du->number);
if (reduced_damage>0) { if (reduced_damage > 0) {
df->person[dt.index].hp -= reduced_damage; 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);
}
} vampirism(at, reduced_damage);
if (df->person[dt.index].hp > 0) { /* Hat <20>berlebt */
if (u_race(au) == get_race(RC_DAEMON)) { ship_damage(b->turn, du);
if (!(df->person[dt.index].flags & (FL_COURAGE | FL_DAZZLED))) {
df->person[dt.index].flags |= FL_DAZZLED;
df->person[dt.index].defence--;
}
}
return false;
} }
/* Sieben Leben */ if (survives(af, dt, b))
if (u_race(du) == get_race(RC_CAT) && (chance(1.0 / 7))) { return false;
df->person[dt.index].hp = unit_max_hp(du);
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; ++at.fighter->kills;
for (pitm = &du->items; *pitm;) { destroy_items(dt);
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;
}
}
kill_troop(dt); kill_troop(dt);
return true; return true;

View File

@ -175,7 +175,6 @@ extern "C" {
int attack; int attack;
int defence; int defence;
int damage; int damage;
int damage_rear;
int flags; int flags;
int speed; int speed;
int reload; int reload;

View File

@ -565,6 +565,38 @@ static void test_battle_skilldiff(CuTest *tc)
test_teardown(); 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) static void test_battle_report_one(CuTest *tc)
{ {
battle * b = NULL; battle * b = NULL;
@ -812,6 +844,7 @@ CuSuite *get_battle_suite(void)
SUITE_ADD_TEST(suite, test_magic_resistance); SUITE_ADD_TEST(suite, test_magic_resistance);
SUITE_ADD_TEST(suite, test_projectile_armor); SUITE_ADD_TEST(suite, test_projectile_armor);
SUITE_ADD_TEST(suite, test_tactics_chance); SUITE_ADD_TEST(suite, test_tactics_chance);
SUITE_ADD_TEST(suite, test_terminate);
DISABLE_TEST(suite, test_drain_exp); DISABLE_TEST(suite, test_drain_exp);
return suite; return suite;
} }