forked from github/server
change all magic resistance to use fractions.
This commit is contained in:
parent
e557140ad1
commit
3a985108a6
36
src/battle.c
36
src/battle.c
|
@ -1059,7 +1059,7 @@ static int rc_specialdamage(const unit *au, const unit *du, const struct weapon_
|
|||
return modifier;
|
||||
}
|
||||
|
||||
int calculate_armor(troop dt, const weapon_type *dwtype, const weapon_type *awtype, double *magres) {
|
||||
int calculate_armor(troop dt, const weapon_type *dwtype, const weapon_type *awtype, variant *magres) {
|
||||
fighter *df = dt.fighter;
|
||||
unit *du = df->unit;
|
||||
int ar = 0, an, am;
|
||||
|
@ -1110,21 +1110,31 @@ int calculate_armor(troop dt, const weapon_type *dwtype, const weapon_type *awty
|
|||
|
||||
if (magres) {
|
||||
/* calculate damage multiplier for magical damage */
|
||||
double res = 1.0 - magic_resistance(du);
|
||||
variant res;
|
||||
|
||||
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 *= (1 - armor->magres);
|
||||
if (shield && fval(shield, ATF_LAEN))
|
||||
res *= (1 - shield->magres);
|
||||
if (dwtype)
|
||||
res *= (1 - dwtype->magres);
|
||||
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 nat<61>rliche und magische R<>stung */
|
||||
/* gegen Magie wirkt nur natuerliche und magische Ruestung */
|
||||
ar = an + am;
|
||||
*magres = res > 0 ? res : 0;
|
||||
if (res.sa[0] >= 0) {
|
||||
*magres = res;
|
||||
}
|
||||
else {
|
||||
*magres = frac_make(0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return ar;
|
||||
|
@ -1147,7 +1157,7 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile)
|
|||
const weapon_type *dwtype = NULL;
|
||||
const weapon_type *awtype = NULL;
|
||||
const weapon *weapon;
|
||||
double res = 1.0;
|
||||
variant res = frac_make(1, 1);
|
||||
|
||||
int rda, sk = 0, sd;
|
||||
bool magic = false;
|
||||
|
@ -1190,9 +1200,9 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile)
|
|||
return false;
|
||||
}
|
||||
|
||||
/* TODO not sure if res could be > 1 here */
|
||||
if (magic) {
|
||||
da = (int)(MAX(da * res, 0));
|
||||
res = frac_mul(frac_make(da, 1), res);
|
||||
da = res.sa[0] / res.sa[1];
|
||||
}
|
||||
|
||||
if (type != AT_COMBATSPELL && type != AT_SPELL) {
|
||||
|
|
|
@ -248,7 +248,7 @@ 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, double *magres);
|
||||
int calculate_armor(troop dt, const struct weapon_type *dwtype, const struct weapon_type *awtype, union variant *magres);
|
||||
bool terminate(troop dt, troop at, int type, const char *damage,
|
||||
bool missile);
|
||||
extern void message_all(battle * b, struct message *m);
|
||||
|
|
|
@ -250,23 +250,24 @@ static void test_calculate_armor(CuTest * tc)
|
|||
armor_type *ashield, *achain;
|
||||
item_type *ibelt, *ishield, *ichain;
|
||||
race *rc;
|
||||
double magres = 0.0;
|
||||
variant magres = frac_zero;
|
||||
variant v50p = frac_make(1, 2);
|
||||
|
||||
test_setup();
|
||||
r = test_create_region(0, 0, 0);
|
||||
ibelt = it_get_or_create(rt_get_or_create("trollbelt"));
|
||||
ishield = it_get_or_create(rt_get_or_create("shield"));
|
||||
ashield = new_armortype(ishield, 0.0, 0.5, 1, ATF_SHIELD);
|
||||
ashield = new_armortype(ishield, 0.0, v50p, 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);
|
||||
achain = new_armortype(ichain, 0.0, v50p, 3, ATF_NONE);
|
||||
wtype = new_weapontype(it_get_or_create(rt_get_or_create("sword")), 0, v50p, 0, 0, 0, 0, SK_MELEE, 1);
|
||||
rc = test_create_race("human");
|
||||
du = test_create_unit(test_create_faction(rc), r);
|
||||
dt.index = 0;
|
||||
|
||||
dt.fighter = setup_fighter(&b, du);
|
||||
CuAssertIntEquals_Msg(tc, "default ac", 0, calculate_armor(dt, 0, 0, &magres));
|
||||
CuAssertDblEquals_Msg(tc, "magres unmodified", 1.0, magres, 0.01);
|
||||
CuAssertIntEquals_Msg(tc, "magres unmodified", magres.sa[0], magres.sa[1]);
|
||||
free_battle(b);
|
||||
|
||||
b = NULL;
|
||||
|
@ -299,13 +300,13 @@ static void test_calculate_armor(CuTest * tc)
|
|||
wtype->flags = WTF_NONE;
|
||||
|
||||
CuAssertIntEquals_Msg(tc, "magical attack", 3, calculate_armor(dt, 0, 0, &magres));
|
||||
CuAssertDblEquals_Msg(tc, "magres unmodified", 1.0, magres, 0.01);
|
||||
CuAssertIntEquals_Msg(tc, "magres unmodified", magres.sa[1], magres.sa[0]);
|
||||
|
||||
ashield->flags |= ATF_LAEN;
|
||||
achain->flags |= ATF_LAEN;
|
||||
magres = 1.0;
|
||||
magres = frac_one;
|
||||
CuAssertIntEquals_Msg(tc, "laen armor", 3, calculate_armor(dt, 0, 0, &magres));
|
||||
CuAssertDblEquals_Msg(tc, "laen magres bonus", 0.25, magres, 0.01);
|
||||
CuAssertIntEquals_Msg(tc, "laen magres bonus", 4, magres.sa[1]);
|
||||
free_battle(b);
|
||||
test_cleanup();
|
||||
}
|
||||
|
@ -319,38 +320,42 @@ static void test_magic_resistance(CuTest *tc)
|
|||
armor_type *ashield, *achain;
|
||||
item_type *ishield, *ichain;
|
||||
race *rc;
|
||||
double magres;
|
||||
variant magres;
|
||||
variant v50p = frac_make(1, 2);
|
||||
variant v10p = frac_make(1, 10);
|
||||
|
||||
test_setup();
|
||||
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);
|
||||
ashield = new_armortype(ishield, 0.0, v50p, 1, ATF_SHIELD);
|
||||
ichain = it_get_or_create(rt_get_or_create("chainmail"));
|
||||
achain = new_armortype(ichain, 0.0, 0.5, 3, ATF_NONE);
|
||||
achain = new_armortype(ichain, 0.0, v50p, 3, ATF_NONE);
|
||||
rc = test_create_race("human");
|
||||
du = test_create_unit(test_create_faction(rc), r);
|
||||
dt.index = 0;
|
||||
|
||||
i_change(&du->items, ishield, 1);
|
||||
dt.fighter = setup_fighter(&b, du);
|
||||
calculate_armor(dt, 0, 0, &magres);
|
||||
CuAssertDblEquals_Msg(tc, "no magres bonus", 0.0, magic_resistance(du), 0.01);
|
||||
CuAssertDblEquals_Msg(tc, "no magres reduction", 1.0, magres, 0.01);
|
||||
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 = 0.1;
|
||||
ashield->magres = v10p;
|
||||
calculate_armor(dt, 0, 0, &magres);
|
||||
CuAssert(tc, "laen reduction => 10%%", frac_equal(frac_make(9, 10), magres));
|
||||
free_battle(b);
|
||||
|
||||
b = NULL;
|
||||
i_change(&du->items, ishield, 1);
|
||||
i_change(&du->items, ichain, 1);
|
||||
achain->flags |= ATF_LAEN;
|
||||
achain->magres = 0.1;
|
||||
achain->magres = v10p;
|
||||
ashield->flags |= ATF_LAEN;
|
||||
ashield->magres = 0.1;
|
||||
ashield->magres = v10p;
|
||||
dt.fighter = setup_fighter(&b, du);
|
||||
calculate_armor(dt, 0, 0, &magres);
|
||||
CuAssertDblEquals_Msg(tc, "laen reduction", 0.81, magres, 0.01);
|
||||
CuAssert(tc, "2x laen reduction => 81%%", frac_equal(frac_make(81, 100), magres));
|
||||
free_battle(b);
|
||||
|
||||
b = NULL;
|
||||
|
@ -359,17 +364,20 @@ static void test_magic_resistance(CuTest *tc)
|
|||
set_level(du, SK_MAGIC, 2);
|
||||
dt.fighter = setup_fighter(&b, du);
|
||||
calculate_armor(dt, 0, 0, &magres);
|
||||
CuAssertDblEquals_Msg(tc, "skill bonus", 0.1, magic_resistance(du), 0.01);
|
||||
CuAssertDblEquals_Msg(tc, "skill reduction", 0.9, magres, 0.01);
|
||||
rc->magres = 50; /* percentage, gets added to skill bonus */
|
||||
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);
|
||||
CuAssertDblEquals_Msg(tc, "race bonus", 0.6, magic_resistance(du), 0.01);
|
||||
CuAssertDblEquals_Msg(tc, "race reduction", 0.4, magres, 0.01);
|
||||
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)));
|
||||
|
||||
rc->magres = 150; /* should not cause negative damage multiplier */
|
||||
CuAssertDblEquals_Msg(tc, "magic resistance is never > 0.9", 0.9, magic_resistance(du), 0.01);
|
||||
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);
|
||||
CuAssertDblEquals_Msg(tc, "damage reduction is never < 0.1", 0.1, magres, 0.01);
|
||||
CuAssert(tc, "damage reduction is never < 0.1", frac_equal(magres, frac_make(1, 10)));
|
||||
|
||||
free_battle(b);
|
||||
test_cleanup();
|
||||
|
@ -385,14 +393,15 @@ static void test_projectile_armor(CuTest * tc)
|
|||
armor_type *ashield, *achain;
|
||||
item_type *ishield, *ichain;
|
||||
race *rc;
|
||||
variant v50p = frac_make(1, 2);
|
||||
|
||||
test_setup();
|
||||
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);
|
||||
ashield = new_armortype(ishield, 0.0, v50p, 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);
|
||||
achain = new_armortype(ichain, 0.0, v50p, 3, ATF_NONE);
|
||||
wtype = new_weapontype(it_get_or_create(rt_get_or_create("sword")), 0, v50p, 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);
|
||||
|
|
|
@ -211,7 +211,7 @@ static void test_tax_cmd(CuTest *tc) {
|
|||
silver = get_resourcetype(R_SILVER)->itype;
|
||||
|
||||
sword = it_get_or_create(rt_get_or_create("sword"));
|
||||
new_weapontype(sword, 0, 0.0, NULL, 0, 0, 0, SK_MELEE, 1);
|
||||
new_weapontype(sword, 0, frac_zero, NULL, 0, 0, 0, SK_MELEE, 1);
|
||||
i_change(&u->items, sword, 1);
|
||||
set_level(u, SK_MELEE, 1);
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ static void test_guard_unskilled(CuTest * tc)
|
|||
|
||||
test_setup();
|
||||
itype = it_get_or_create(rt_get_or_create("sword"));
|
||||
new_weapontype(itype, 0, 0.0, NULL, 0, 0, 0, SK_MELEE, 2);
|
||||
new_weapontype(itype, 0, frac_zero, NULL, 0, 0, 0, SK_MELEE, 2);
|
||||
r = test_create_region(0, 0, 0);
|
||||
u = test_create_unit(test_create_faction(0), r);
|
||||
ug = test_create_unit(test_create_faction(0), r);
|
||||
|
@ -61,7 +61,7 @@ static void test_guard_armed(CuTest * tc)
|
|||
|
||||
test_setup();
|
||||
itype = it_get_or_create(rt_get_or_create("sword"));
|
||||
new_weapontype(itype, 0, 0.0, NULL, 0, 0, 0, SK_MELEE, 2);
|
||||
new_weapontype(itype, 0, frac_zero, NULL, 0, 0, 0, SK_MELEE, 2);
|
||||
r = test_create_region(0, 0, 0);
|
||||
u = test_create_unit(test_create_faction(0), r);
|
||||
ug = test_create_unit(test_create_faction(0), r);
|
||||
|
@ -80,7 +80,7 @@ static void test_is_guard(CuTest * tc)
|
|||
|
||||
test_setup();
|
||||
itype = it_get_or_create(rt_get_or_create("sword"));
|
||||
new_weapontype(itype, 0, 0.0, NULL, 0, 0, 0, SK_MELEE, 2);
|
||||
new_weapontype(itype, 0, frac_zero, NULL, 0, 0, 0, SK_MELEE, 2);
|
||||
r = test_create_region(0, 0, 0);
|
||||
ug = test_create_unit(test_create_faction(0), r);
|
||||
i_change(&ug->items, itype, 1);
|
||||
|
|
|
@ -60,7 +60,7 @@ extern "C" {
|
|||
int capacity; /* Kapazit<69>t pro Gr<47><72>enpunkt */
|
||||
int maxcapacity; /* Max. Kapazit<69>t */
|
||||
int maxsize; /* how big can it get, with all the extensions? */
|
||||
int magres; /* how well it resists against spells */
|
||||
variant magres; /* how well it resists against spells */
|
||||
int magresbonus; /* bonus it gives the target against spells */
|
||||
int fumblebonus; /* bonus that reduces fumbling */
|
||||
double auraregen; /* modifier for aura regeneration inside building */
|
||||
|
|
|
@ -381,7 +381,7 @@ static void test_btype_defaults(CuTest *tc) {
|
|||
CuAssertIntEquals(tc, -1, btype->maxsize);
|
||||
CuAssertIntEquals(tc, 1, btype->capacity);
|
||||
CuAssertIntEquals(tc, -1, btype->maxcapacity);
|
||||
CuAssertIntEquals(tc, 0, btype->magres);
|
||||
CuAssertIntEquals(tc, 0, btype->magres.sa[0]);
|
||||
CuAssertIntEquals(tc, 0, btype->magresbonus);
|
||||
CuAssertIntEquals(tc, 0, btype->fumblebonus);
|
||||
CuAssertIntEquals(tc, 0, btype->flags);
|
||||
|
|
|
@ -275,7 +275,7 @@ luxury_type *new_luxurytype(item_type * itype, int price)
|
|||
}
|
||||
|
||||
weapon_type *new_weapontype(item_type * itype,
|
||||
int wflags, double magres, const char *damage[], int offmod, int defmod,
|
||||
int wflags, variant magres, const char *damage[], int offmod, int defmod,
|
||||
int reload, skill_t sk, int minskill)
|
||||
{
|
||||
weapon_type *wtype;
|
||||
|
@ -301,7 +301,7 @@ weapon_type *new_weapontype(item_type * itype,
|
|||
return wtype;
|
||||
}
|
||||
|
||||
armor_type *new_armortype(item_type * itype, double penalty, double magres,
|
||||
armor_type *new_armortype(item_type * itype, double penalty, variant magres,
|
||||
int prot, unsigned int flags)
|
||||
{
|
||||
armor_type *atype;
|
||||
|
|
|
@ -181,7 +181,7 @@ extern "C" {
|
|||
const item_type *itype;
|
||||
unsigned int flags;
|
||||
double penalty;
|
||||
double magres;
|
||||
variant magres;
|
||||
int prot;
|
||||
float projectile; /* chance, dass ein projektil abprallt */
|
||||
} armor_type;
|
||||
|
@ -205,7 +205,7 @@ extern "C" {
|
|||
int minskill;
|
||||
int offmod;
|
||||
int defmod;
|
||||
double magres;
|
||||
variant magres;
|
||||
int reload; /* time to reload this weapon */
|
||||
weapon_mod *modifiers;
|
||||
/* --- functions --- */
|
||||
|
@ -244,14 +244,12 @@ extern "C" {
|
|||
/* creation */
|
||||
resource_type *rt_get_or_create(const char *name);
|
||||
item_type *it_get_or_create(resource_type *rtype);
|
||||
item_type *new_itemtype(resource_type * rtype, int iflags, int weight,
|
||||
int capacity);
|
||||
luxury_type *new_luxurytype(item_type * itype, int price);
|
||||
weapon_type *new_weapontype(item_type * itype, int wflags,
|
||||
double magres, const char *damage[], int offmod, int defmod, int reload,
|
||||
variant magres, const char *damage[], int offmod, int defmod, int reload,
|
||||
skill_t sk, int minskill);
|
||||
armor_type *new_armortype(item_type * itype, double penalty,
|
||||
double magres, int prot, unsigned int flags);
|
||||
variant magres, int prot, unsigned int flags);
|
||||
potion_type *new_potiontype(item_type * itype, int level);
|
||||
|
||||
typedef enum {
|
||||
|
|
|
@ -460,7 +460,7 @@ static void json_race(cJSON *json, race *rc) {
|
|||
break;
|
||||
case cJSON_Number:
|
||||
if (strcmp(child->string, "magres") == 0) {
|
||||
rc->magres = child->valueint;
|
||||
rc->magres = frac_make(child->valueint, 100);
|
||||
}
|
||||
else if (strcmp(child->string, "maxaura") == 0) {
|
||||
rc->maxaura = child->valueint;
|
||||
|
|
|
@ -163,8 +163,7 @@ static void test_races(CuTest * tc)
|
|||
CuAssertPtrNotNull(tc, rc);
|
||||
CuAssertIntEquals(tc, RCF_NPC | RCF_WALK | RCF_UNDEAD, rc->flags);
|
||||
CuAssertStrEquals(tc, "1d4", rc->def_damage);
|
||||
CuAssertIntEquals(tc, 100, rc->magres);
|
||||
CuAssertDblEquals(tc, 1.0, rc_magres(rc), 0.0);
|
||||
CuAssertTrue(tc, frac_equal(frac_one, rc->magres));
|
||||
CuAssertIntEquals(tc, 200, rc->maxaura);
|
||||
CuAssertDblEquals(tc, 2.0, rc_maxaura(rc), 0.0);
|
||||
CuAssertDblEquals(tc, 3.0, rc->regaura, 0.0);
|
||||
|
|
|
@ -331,6 +331,7 @@ race *rc_create(const char *zName)
|
|||
|
||||
assert(zName);
|
||||
rc = (race *)calloc(sizeof(race), 1);
|
||||
rc->magres.sa[1] = 1;
|
||||
rc->hitpoints = 1;
|
||||
rc->weight = PERSON_WEIGHT;
|
||||
rc->capacity = 540;
|
||||
|
@ -383,8 +384,8 @@ bool r_insectstalled(const region * r)
|
|||
return fval(r->terrain, ARCTIC_REGION);
|
||||
}
|
||||
|
||||
double rc_magres(const race *rc) {
|
||||
return rc->magres / 100.0;
|
||||
variant rc_magres(const race *rc) {
|
||||
return rc->magres;
|
||||
}
|
||||
|
||||
double rc_maxaura(const race *rc) {
|
||||
|
|
|
@ -116,7 +116,7 @@ extern "C" {
|
|||
|
||||
typedef struct race {
|
||||
char *_name;
|
||||
int magres;
|
||||
variant magres;
|
||||
int healing;
|
||||
int maxaura; /* Faktor auf Maximale Aura */
|
||||
double regaura; /* Faktor auf Regeneration */
|
||||
|
@ -182,7 +182,7 @@ extern "C" {
|
|||
|
||||
int rc_luxury_trade(const struct race *rc);
|
||||
int rc_herb_trade(const struct race *rc);
|
||||
double rc_magres(const struct race *rc);
|
||||
variant rc_magres(const struct race *rc);
|
||||
double rc_maxaura(const struct race *rc);
|
||||
int rc_armor_bonus(const struct race *rc);
|
||||
int rc_scare(const struct race *rc);
|
||||
|
|
|
@ -24,8 +24,7 @@ static void test_rc_defaults(CuTest *tc) {
|
|||
rc = rc_get_or_create("human");
|
||||
CuAssertStrEquals(tc, "human", rc->_name);
|
||||
CuAssertIntEquals(tc, 0, rc_armor_bonus(rc));
|
||||
CuAssertIntEquals(tc, 0, rc->magres);
|
||||
CuAssertDblEquals(tc, 0.0, rc_magres(rc), 0.0);
|
||||
CuAssertIntEquals(tc, 0, rc->magres.sa[0]);
|
||||
CuAssertIntEquals(tc, 0, rc->healing);
|
||||
CuAssertDblEquals(tc, 0.0, rc_maxaura(rc), 0.0);
|
||||
CuAssertDblEquals(tc, 1.0, rc->recruit_multi, 0.0);
|
||||
|
|
|
@ -61,6 +61,19 @@ without prior permission by the authors of Eressea.
|
|||
#include <string.h>
|
||||
|
||||
#ifdef USE_LIBXML2
|
||||
|
||||
static variant xml_fraction(xmlNodePtr node, const char *name) {
|
||||
xmlChar *propValue = xmlGetProp(node, BAD_CAST name);
|
||||
if (propValue != NULL) {
|
||||
int num, den = 100;
|
||||
double fval = atof((const char *)propValue);
|
||||
num = (int)(fval * den + 0.5);
|
||||
xmlFree(propValue);
|
||||
return frac_make(num, den);
|
||||
}
|
||||
return frac_make(0, 1);
|
||||
}
|
||||
|
||||
static void xml_readtext(xmlNodePtr node, struct locale **lang, xmlChar ** text)
|
||||
{
|
||||
xmlChar *propValue = xmlGetProp(node, BAD_CAST "locale");
|
||||
|
@ -245,7 +258,7 @@ static int parse_buildings(xmlDocPtr doc)
|
|||
btype->maxcapacity = xml_ivalue(node, "maxcapacity", btype->maxcapacity);
|
||||
btype->maxsize = xml_ivalue(node, "maxsize", btype->maxsize);
|
||||
|
||||
btype->magres = xml_ivalue(node, "magres", btype->magres);
|
||||
btype->magres = frac_make(xml_ivalue(node, "magres", 0), 100);
|
||||
btype->magresbonus = xml_ivalue(node, "magresbonus", btype->magresbonus);
|
||||
btype->fumblebonus = xml_ivalue(node, "fumblebonus", btype->fumblebonus);
|
||||
btype->auraregen = xml_fvalue(node, "auraregen", btype->auraregen);
|
||||
|
@ -582,7 +595,7 @@ static armor_type *xml_readarmor(xmlXPathContextPtr xpath, item_type * itype)
|
|||
unsigned int flags = ATF_NONE;
|
||||
int ac = xml_ivalue(node, "ac", 0);
|
||||
double penalty = xml_fvalue(node, "penalty", 0.0);
|
||||
double magres = xml_fvalue(node, "magres", 0.0);
|
||||
variant magres = xml_fraction(node, "magres");
|
||||
|
||||
if (xml_bvalue(node, "laen", false))
|
||||
flags |= ATF_LAEN;
|
||||
|
@ -607,7 +620,7 @@ static weapon_type *xml_readweapon(xmlXPathContextPtr xpath, item_type * itype)
|
|||
int offmod = xml_ivalue(node, "offmod", 0);
|
||||
int defmod = xml_ivalue(node, "defmod", 0);
|
||||
int reload = xml_ivalue(node, "reload", 0);
|
||||
double magres = xml_fvalue(node, "magres", 0.0);
|
||||
variant magres = xml_fraction(node, "magres");
|
||||
|
||||
if (xml_bvalue(node, "armorpiercing", false))
|
||||
flags |= WTF_ARMORPIERCING;
|
||||
|
@ -899,22 +912,6 @@ static int parse_rules(xmlDocPtr doc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int gcd(int num, int den) {
|
||||
const int primes[] = { 3, 5, 7, 11, 0 };
|
||||
int i=0, g = 1, p = 2;
|
||||
while (p && p<=den && p<=num) {
|
||||
if (num % p == 0 && den % p == 0) {
|
||||
num /= p;
|
||||
den /= p;
|
||||
g *= p;
|
||||
}
|
||||
else {
|
||||
p = primes[i++];
|
||||
}
|
||||
}
|
||||
return g;
|
||||
}
|
||||
|
||||
static int parse_resources(xmlDocPtr doc)
|
||||
{
|
||||
xmlXPathContextPtr xpath = xmlXPathNewContext(doc);
|
||||
|
@ -1031,15 +1028,7 @@ static int parse_resources(xmlDocPtr doc)
|
|||
rdata->modifiers[k].flags = RMF_SKILL;
|
||||
}
|
||||
else if (strcmp((const char *)propValue, "material") == 0) {
|
||||
int g, num, den = 100;
|
||||
double fval = xml_fvalue(node, "value", 0);
|
||||
/* TODO: extract into a function for reading fractions? */
|
||||
num = (int)(fval * den + 0.5);
|
||||
g = gcd(num, den);
|
||||
num /= g;
|
||||
den /= g;
|
||||
rdata->modifiers[k].value.sa[0] = (short)num;
|
||||
rdata->modifiers[k].value.sa[1] = (short)den;
|
||||
rdata->modifiers[k].value = xml_fraction(node, "value");
|
||||
rdata->modifiers[k].flags = RMF_SAVEMATERIAL;
|
||||
}
|
||||
else if (strcmp((const char *)propValue, "require") == 0) {
|
||||
|
@ -1623,7 +1612,7 @@ static int parse_races(xmlDocPtr doc)
|
|||
rc->def_damage = strdup((const char *)propValue);
|
||||
xmlFree(propValue);
|
||||
|
||||
rc->magres = xml_ivalue(node, "magres", rc->magres);
|
||||
rc->magres = frac_make(xml_ivalue(node, "magres", 100), 100);
|
||||
rc->healing = (int)(xml_fvalue(node, "healing", rc->healing) * 100); /* TODO: store as int in XML */
|
||||
rc->maxaura = (int)(xml_fvalue(node, "maxaura", rc->maxaura) * 100); /* TODO: store as int in XML */
|
||||
rc->regaura = (float)xml_fvalue(node, "regaura", rc->regaura);
|
||||
|
|
|
@ -611,7 +611,7 @@ void setup_guard(guard_fixture *fix, bool armed) {
|
|||
if (armed) {
|
||||
item_type *itype;
|
||||
itype = it_get_or_create(rt_get_or_create("sword"));
|
||||
new_weapontype(itype, 0, 0.0, NULL, 0, 0, 0, SK_MELEE, 2);
|
||||
new_weapontype(itype, 0, frac_zero, NULL, 0, 0, 0, SK_MELEE, 2);
|
||||
i_change(&u->items, itype, 1);
|
||||
set_level(u, SK_MELEE, 2);
|
||||
}
|
||||
|
@ -1481,7 +1481,7 @@ static void test_armedmen(CuTest *tc) {
|
|||
test_setup();
|
||||
u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0));
|
||||
it_sword = test_create_itemtype("sword");
|
||||
wtype = new_weapontype(it_sword, 0, 0.5, 0, 0, 0, 0, SK_MELEE, 1);
|
||||
wtype = new_weapontype(it_sword, 0, frac_make(1, 2), 0, 0, 0, 0, SK_MELEE, 1);
|
||||
CuAssertIntEquals(tc, 0, armedmen(u, false));
|
||||
CuAssertIntEquals(tc, 0, armedmen(u, true));
|
||||
set_level(u, SK_MELEE, 1);
|
||||
|
|
88
src/magic.c
88
src/magic.c
|
@ -1135,27 +1135,29 @@ static int farcasting(unit * magician, region * r)
|
|||
|
||||
/* allgemeine Magieresistenz einer Einheit,
|
||||
* reduziert magischen Schaden */
|
||||
double magic_resistance(unit * target)
|
||||
variant magic_resistance(unit * target)
|
||||
{
|
||||
attrib *a;
|
||||
curse *c;
|
||||
const curse_type * ct_goodresist = 0, *ct_badresist = 0;
|
||||
const resource_type *rtype;
|
||||
const race *rc = u_race(target);
|
||||
double probability = rc_magres(rc);
|
||||
variant v, prob = rc_magres(rc);
|
||||
const plane *pl = rplane(target->region);
|
||||
|
||||
if (rc == get_race(RC_HIRNTOETER) && !pl) {
|
||||
probability /= 2;
|
||||
prob.sa[1] *= 2;
|
||||
}
|
||||
assert(target->number > 0);
|
||||
/* Magier haben einen Resistenzbonus vom Magietalent * 5% */
|
||||
probability += effskill(target, SK_MAGIC, 0) * 0.05;
|
||||
prob = frac_add(prob, frac_make(effskill(target, SK_MAGIC, 0), 20));
|
||||
|
||||
/* Auswirkungen von Zaubern auf der Einheit */
|
||||
c = get_curse(target->attribs, ct_find("magicresistance"));
|
||||
if (c) {
|
||||
probability += 0.01 * curse_geteffect(c) * get_cursedmen(target, c);
|
||||
/* TODO: legacy. magicresistance-effect is an integer-percentage stored in a double */
|
||||
int effect = curse_geteffect_int(c) * get_cursedmen(target, c);
|
||||
prob = frac_add(prob, frac_make(effect, 100));
|
||||
}
|
||||
|
||||
/* Unicorn +10 */
|
||||
|
@ -1163,7 +1165,7 @@ double magic_resistance(unit * target)
|
|||
if (rtype) {
|
||||
int n = i_get(target->items, rtype->itype);
|
||||
if (n) {
|
||||
probability += n * 0.1 / target->number;
|
||||
prob = frac_add(prob, frac_make(n, target->number * 10));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1180,13 +1182,15 @@ double magic_resistance(unit * target)
|
|||
if (mage != NULL) {
|
||||
if (ct_goodresist && c->type == ct_goodresist) {
|
||||
if (alliedunit(mage, target->faction, HELP_GUARD)) {
|
||||
probability += curse_geteffect(c) * 0.01;
|
||||
/* TODO: legacy. magicresistance-effect is an integer-percentage stored in a double */
|
||||
prob = frac_add(prob, frac_make(curse_geteffect_int(c), 100));
|
||||
ct_goodresist = 0; /* only one effect per region */
|
||||
}
|
||||
}
|
||||
else if (ct_badresist && c->type == ct_badresist) {
|
||||
if (!alliedunit(mage, target->faction, HELP_GUARD)) {
|
||||
probability -= curse_geteffect(c) * 0.01;
|
||||
/* TODO: legacy. magicresistance-effect is an integer-percentage stored in a double */
|
||||
prob = frac_sub(prob, frac_make(curse_geteffect_int(c), 100));
|
||||
ct_badresist = 0; /* only one effect per region */
|
||||
}
|
||||
}
|
||||
|
@ -1202,11 +1206,18 @@ double magic_resistance(unit * target)
|
|||
const struct building_type *btype = building_is_active(b) ? b->type : NULL;
|
||||
|
||||
/* gesegneter Steinkreis gibt 30% dazu */
|
||||
if (btype)
|
||||
probability += btype->magresbonus * 0.01;
|
||||
if (btype) {
|
||||
/* TODO: legacy. building-bonus is an integer-percentage */
|
||||
prob = frac_add(prob, frac_make(btype->magresbonus, 100));
|
||||
}
|
||||
}
|
||||
|
||||
return (probability<0.9) ? probability : 0.9;
|
||||
/* resistance must never be more than 90% */
|
||||
v = frac_make(9, 10);
|
||||
if (frac_sign(frac_sub(prob, v)) > 0) { /* prob < 90% */
|
||||
return v; /* at most 90% */
|
||||
}
|
||||
return prob;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
@ -1223,12 +1234,12 @@ double magic_resistance(unit * target)
|
|||
bool
|
||||
target_resists_magic(unit * magician, void *obj, int objtyp, int t_bonus)
|
||||
{
|
||||
double probability = 0.0;
|
||||
variant v02p, v98p, prob = frac_make(t_bonus, 100);
|
||||
attrib *a = NULL;
|
||||
|
||||
if (magician == NULL)
|
||||
return true;
|
||||
if (obj == NULL)
|
||||
if (magician == NULL || obj == NULL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (objtyp) {
|
||||
case TYP_UNIT:
|
||||
|
@ -1248,43 +1259,56 @@ target_resists_magic(unit * magician, void *obj, int objtyp, int t_bonus)
|
|||
pa = sk;
|
||||
}
|
||||
|
||||
/* Contest */
|
||||
probability = 0.05 * (10 + pa - at);
|
||||
|
||||
probability += magic_resistance((unit *)obj);
|
||||
/* Contest, probability = 0.05 * (10 + pa - at); */
|
||||
prob = frac_add(prob, frac_make(10 + pa - at, 20));
|
||||
prob = frac_add(prob, magic_resistance((unit *)obj));
|
||||
break;
|
||||
}
|
||||
|
||||
case TYP_REGION:
|
||||
/* Bonus durch Zauber */
|
||||
/* Bonus durch Zauber
|
||||
probability +=
|
||||
0.01 * get_curseeffect(((region *)obj)->attribs, C_RESIST_MAGIC, 0);
|
||||
0.01 * get_curseeffect(((region *)obj)->attribs, C_RESIST_MAGIC, 0); */
|
||||
a = ((region *)obj)->attribs;
|
||||
break;
|
||||
|
||||
case TYP_BUILDING:
|
||||
/* Bonus durch Zauber */
|
||||
/* Bonus durch Zauber
|
||||
probability +=
|
||||
0.01 * get_curseeffect(((building *)obj)->attribs, C_RESIST_MAGIC, 0);
|
||||
|
||||
/* Bonus durch Typ */
|
||||
probability += 0.01 * ((building *)obj)->type->magres;
|
||||
|
||||
0.01 * get_curseeffect(((building *)obj)->attribs, C_RESIST_MAGIC, 0); */
|
||||
a = ((building *)obj)->attribs;
|
||||
/* Bonus durch Typ
|
||||
probability += 0.01 * ((building *)obj)->type->magres; */
|
||||
prob = frac_add(prob, ((building *)obj)->type->magres);
|
||||
break;
|
||||
|
||||
case TYP_SHIP:
|
||||
/* Bonus durch Zauber */
|
||||
probability +=
|
||||
0.01 * get_curseeffect(((ship *)obj)->attribs, C_RESIST_MAGIC, 0);
|
||||
a = ((ship *)obj)->attribs;
|
||||
break;
|
||||
}
|
||||
|
||||
probability = MAX(0.02, probability + t_bonus * 0.01);
|
||||
probability = MIN(0.98, probability);
|
||||
if (a) {
|
||||
const struct curse_type *ct_resist = ct_find(oldcursename(C_RESIST_MAGIC));
|
||||
curse * c = get_curse(a, ct_resist);
|
||||
int effect = curse_geteffect_int(c);
|
||||
prob = frac_add(prob, frac_make(effect, 100));
|
||||
}
|
||||
|
||||
/* ignore results < 2% and > 98% */
|
||||
v02p = frac_make(1, 50);
|
||||
v98p = frac_make(49, 50);
|
||||
if (frac_sign(frac_sub(prob, v02p)) < 0) {
|
||||
prob = v02p;
|
||||
}
|
||||
else if (frac_sign(frac_sub(prob, v98p)) > 0) {
|
||||
prob = v98p;
|
||||
}
|
||||
|
||||
/* gibt true, wenn die Zufallszahl kleiner als die chance ist und
|
||||
* false, wenn sie gleich oder größer ist, dh je größer die
|
||||
* Magieresistenz (chance) desto eher gibt die Funktion true zurück */
|
||||
return chance(probability);
|
||||
return rng_int() % prob.sa[1] < prob.sa[0];
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
|
|
@ -315,7 +315,7 @@ extern "C" {
|
|||
bool is_magic_resistant(struct unit *magician, struct unit *target, int
|
||||
resist_bonus);
|
||||
/* Mapperfunktion für target_resists_magic() vom Typ struct unit. */
|
||||
extern double magic_resistance(struct unit *target);
|
||||
variant magic_resistance(struct unit *target);
|
||||
/* gibt die Chance an, mit der einem Zauber widerstanden wird. Je
|
||||
* größer, desto resistenter ist da Opfer */
|
||||
bool target_resists_magic(struct unit *magician, void *obj, int objtyp,
|
||||
|
|
|
@ -414,15 +414,15 @@ static void test_magic_resistance(CuTest *tc) {
|
|||
test_setup();
|
||||
rc = test_create_race("human");
|
||||
u = test_create_unit(test_create_faction(rc), test_create_region(0, 0, 0));
|
||||
CuAssertDblEquals(tc, rc->magres/100.0, magic_resistance(u), 0.01);
|
||||
rc->magres = 100;
|
||||
CuAssertDblEquals_Msg(tc, "magic resistance is capped at 0.9", 0.9, magic_resistance(u), 0.01);
|
||||
CuAssertTrue(tc, frac_equal(rc->magres, magic_resistance(u)));
|
||||
rc->magres = frac_one;
|
||||
CuAssert(tc, "magic resistance is capped at 0.9", frac_equal(magic_resistance(u), frac_make(9, 10)));
|
||||
rc = test_create_race("braineater");
|
||||
rc->magres = 100;
|
||||
rc->magres = frac_one;
|
||||
u_setrace(u, rc);
|
||||
CuAssertDblEquals_Msg(tc, "brain eaters outside astral space have 50% magres", 0.5, magic_resistance(u), 0.01);
|
||||
CuAssert(tc, "brain eaters outside astral space have 50% magres", frac_equal(magic_resistance(u), frac_make(1, 2)));
|
||||
u->region->_plane = get_astralplane();
|
||||
CuAssertDblEquals_Msg(tc, "brain eaters in astral space have full magres", 0.9, magic_resistance(u), 0.01);
|
||||
CuAssert(tc, "brain eaters in astral space have full magres", frac_equal(magic_resistance(u), frac_make(9, 10)));
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
|
|
|
@ -2851,7 +2851,8 @@ static int dc_age(struct curse *c)
|
|||
while (*up != NULL) {
|
||||
unit *u = *up;
|
||||
int hp;
|
||||
double damage = c->effect * u->number;
|
||||
variant dmg = frac_make(u->number, 1);
|
||||
double damage = c->effect;
|
||||
|
||||
if (u->number <= 0 || target_resists_magic(mage, u, TYP_UNIT, 0)) {
|
||||
up = &u->next;
|
||||
|
@ -2859,7 +2860,9 @@ static int dc_age(struct curse *c)
|
|||
}
|
||||
|
||||
/* Reduziert durch Magieresistenz */
|
||||
damage *= (1.0 - magic_resistance(u));
|
||||
dmg = frac_mul(dmg, frac_sub(frac_make(1,1), magic_resistance(u)));
|
||||
damage *= dmg.sa[0];
|
||||
damage /= dmg.sa[1];
|
||||
hp = change_hitpoints(u, -(int)damage);
|
||||
|
||||
ADDMSG(&u->faction->msgs, msg_message((hp>0)?"poison_damage":"poison_death", "region unit", r, u));
|
||||
|
|
|
@ -71,7 +71,7 @@ static void test_all_spy_message(CuTest *tc) {
|
|||
set_factionstealth(fix.victim, fix.spy->faction);
|
||||
|
||||
itype = it_get_or_create(rt_get_or_create("sword"));
|
||||
new_weapontype(itype, 0, 0.0, NULL, 0, 0, 0, SK_MELEE, 2);
|
||||
new_weapontype(itype, 0, frac_zero, NULL, 0, 0, 0, SK_MELEE, 2);
|
||||
i_change(&fix.victim->items, itype, 1);
|
||||
|
||||
spy_message(99, fix.spy, fix.victim);
|
||||
|
|
|
@ -80,7 +80,6 @@ damage_unit(unit * u, const char *dam, bool physical, bool magic)
|
|||
int *hp, hpstack[20];
|
||||
int h;
|
||||
int i, dead = 0, hp_rem = 0, heiltrank;
|
||||
double magres = magic_resistance(u);
|
||||
|
||||
assert(u->number);
|
||||
if (fval(u_race(u), RCF_ILLUSIONARY) || u_race(u) == get_race(RC_SPELL)) {
|
||||
|
@ -105,10 +104,14 @@ damage_unit(unit * u, const char *dam, bool physical, bool magic)
|
|||
/* Schaden */
|
||||
for (i = 0; i < u->number; i++) {
|
||||
int damage = dice_rand(dam);
|
||||
if (magic)
|
||||
damage = (int)(damage * (1.0 - magres));
|
||||
if (physical)
|
||||
if (magic) {
|
||||
variant magres = magic_resistance(u);
|
||||
magres = frac_sub(frac_make(1, 1), magres);
|
||||
damage = damage * magres.sa[0] / magres.sa[1];
|
||||
}
|
||||
if (physical) {
|
||||
damage -= nb_armor(u, i);
|
||||
}
|
||||
hp[i] -= damage;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue