forked from github/server
refactor terminate finished
This commit is contained in:
parent
d981044953
commit
69702df203
291
src/battle.c
291
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;
|
||||
|
@ -963,6 +966,16 @@ static void vampirism(troop at, int damage)
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,74 +1127,100 @@ static bool resurrect_troop(troop dt)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
terminate(troop dt, troop at, int type, const char *damage_formula, bool missile)
|
||||
{
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
const weapon_type *dwtype = NULL;
|
||||
const weapon_type *awtype = NULL;
|
||||
}
|
||||
|
||||
static void calculate_defence_type(troop dt, troop at, int type, bool missile,
|
||||
const weapon_type **dwtype, int *defskill) {
|
||||
const weapon *weapon;
|
||||
const armor_type *armor = NULL;
|
||||
const armor_type *shield = NULL;
|
||||
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;
|
||||
}
|
||||
|
||||
int reduced_damage, attskill = 0, defskill = 0;
|
||||
bool magic = false;
|
||||
|
||||
int damage = dice_rand(damage_formula);
|
||||
|
||||
assert(du->number > 0);
|
||||
++at.fighter->hits;
|
||||
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);
|
||||
*attskill = weapon_effskill(at, dt, weapon, true, missile);
|
||||
if (weapon)
|
||||
awtype = weapon->type;
|
||||
if (awtype && fval(awtype, WTF_MAGICAL))
|
||||
magic = true;
|
||||
*awtype = weapon->type;
|
||||
if (*awtype && fval(*awtype, WTF_MAGICAL))
|
||||
*magic = true;
|
||||
break;
|
||||
case AT_NATURAL:
|
||||
attskill = weapon_effskill(at, dt, NULL, true, missile);
|
||||
*attskill = weapon_effskill(at, dt, NULL, true, missile);
|
||||
break;
|
||||
case AT_SPELL:
|
||||
case AT_COMBATSPELL:
|
||||
magic = true;
|
||||
*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)
|
||||
&& !fval(awtype, WTF_MISSILE)))) {
|
||||
damage += CavalryBonus(au, dt, BONUS_DAMAGE);
|
||||
}
|
||||
|
||||
armor = select_armor(dt, false);
|
||||
shield = select_armor(dt, true);
|
||||
|
||||
armor_value = calculate_armor(dt, dwtype, awtype, armor, shield, magic);
|
||||
if (armor_value < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
damage = apply_resistance(damage, dt, dwtype, armor, shield, magic);
|
||||
|
||||
if (type != AT_COMBATSPELL && type != AT_SPELL) {
|
||||
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;
|
||||
|
@ -1193,35 +1232,16 @@ terminate(troop dt, troop at, int type, const char *damage_formula, bool missile
|
|||
damage += dice_rand(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 {
|
||||
/* melee bonus */
|
||||
if (rule_damage & DAMAGE_MELEE_BONUS) {
|
||||
damage += af->person[at.index].damage;
|
||||
}
|
||||
return damage;
|
||||
}
|
||||
|
||||
/* Skilldifferenzbonus */
|
||||
if (rule_damage & DAMAGE_SKILL_BONUS) {
|
||||
damage += MAX(0, (attskill - defskill) / DAMAGE_QUOTIENT);
|
||||
}
|
||||
}
|
||||
|
||||
reduced_damage = MAX(damage - armor_value, 0);
|
||||
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 {
|
||||
int qi;
|
||||
selist *ql;
|
||||
unsigned int i = 0;
|
||||
|
||||
if (u_race(du)->battle_flags & BF_RES_PIERCE)
|
||||
|
@ -1233,6 +1253,18 @@ terminate(troop dt, troop at, int type, const char *damage_formula, bool missile
|
|||
|
||||
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)) {
|
||||
|
@ -1253,60 +1285,91 @@ terminate(troop dt, troop at, int type, const char *damage_formula, bool missile
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return reduced_damage;
|
||||
}
|
||||
|
||||
bool
|
||||
terminate(troop dt, troop at, int type, const char *damage_formula, bool missile)
|
||||
{
|
||||
fighter *df = dt.fighter;
|
||||
fighter *af = at.fighter;
|
||||
unit *au = af->unit;
|
||||
unit *du = df->unit;
|
||||
battle *b = df->side->battle;
|
||||
|
||||
int armor_value;
|
||||
|
||||
const weapon_type *dwtype = NULL;
|
||||
const weapon_type *awtype = NULL;
|
||||
const armor_type *armor = NULL;
|
||||
const armor_type *shield = NULL;
|
||||
|
||||
int reduced_damage, attskill = 0, defskill = 0;
|
||||
bool magic = false;
|
||||
|
||||
int damage = dice_rand(damage_formula);
|
||||
|
||||
assert(du->number > 0);
|
||||
++at.fighter->hits;
|
||||
|
||||
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)))) {
|
||||
damage += CavalryBonus(au, dt, BONUS_DAMAGE);
|
||||
}
|
||||
|
||||
armor = select_armor(dt, false);
|
||||
shield = select_armor(dt, true);
|
||||
|
||||
armor_value = calculate_armor(dt, dwtype, awtype, armor, shield, magic);
|
||||
if (armor_value < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
damage = apply_resistance(damage, dt, dwtype, armor, shield, magic);
|
||||
|
||||
if (type != AT_COMBATSPELL && type != AT_SPELL) {
|
||||
damage += crit_damage(attskill, defskill, damage_formula);
|
||||
|
||||
damage += rc_specialdamage(au, du, awtype);
|
||||
|
||||
if (awtype == NULL || !fval(awtype, WTF_MISSILE)) {
|
||||
/* melee bonus */
|
||||
if (rule_damage & DAMAGE_MELEE_BONUS) {
|
||||
damage += af->person[at.index].damage;
|
||||
}
|
||||
}
|
||||
|
||||
/* Skilldifferenzbonus */
|
||||
if (rule_damage & DAMAGE_SKILL_BONUS) {
|
||||
damage += MAX(0, (attskill - defskill) / DAMAGE_QUOTIENT);
|
||||
}
|
||||
}
|
||||
|
||||
reduced_damage = MAX(damage - armor_value, 0);
|
||||
|
||||
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) {
|
||||
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);
|
||||
|
||||
ship_damage(b->turn, du);
|
||||
}
|
||||
|
||||
}
|
||||
if (df->person[dt.index].hp > 0) { /* Hat <20>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--;
|
||||
}
|
||||
}
|
||||
if (survives(af, dt, b))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
|
|
@ -175,7 +175,6 @@ extern "C" {
|
|||
int attack;
|
||||
int defence;
|
||||
int damage;
|
||||
int damage_rear;
|
||||
int flags;
|
||||
int speed;
|
||||
int reload;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue