change all magic resistance to use fractions.

This commit is contained in:
Enno Rehling 2017-02-24 20:47:47 +01:00
parent e557140ad1
commit 3a985108a6
22 changed files with 178 additions and 143 deletions

View File

@ -1059,7 +1059,7 @@ static int rc_specialdamage(const unit *au, const unit *du, const struct weapon_
return modifier; 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; fighter *df = dt.fighter;
unit *du = df->unit; unit *du = df->unit;
int ar = 0, an, am; int ar = 0, an, am;
@ -1110,21 +1110,31 @@ int calculate_armor(troop dt, const weapon_type *dwtype, const weapon_type *awty
if (magres) { if (magres) {
/* calculate damage multiplier for magical damage */ /* 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) { if (u_race(du)->battle_flags & BF_EQUIPMENT) {
/* der Effekt von Laen steigt nicht linear */ /* der Effekt von Laen steigt nicht linear */
if (armor && fval(armor, ATF_LAEN)) if (armor && fval(armor, ATF_LAEN)) {
res *= (1 - armor->magres); res = frac_mul(res, frac_sub(frac_one, armor->magres));
if (shield && fval(shield, ATF_LAEN)) }
res *= (1 - shield->magres); if (shield && fval(shield, ATF_LAEN)) {
if (dwtype) res = frac_mul(res, frac_sub(frac_one, shield->magres));
res *= (1 - dwtype->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; ar = an + am;
*magres = res > 0 ? res : 0; if (res.sa[0] >= 0) {
*magres = res;
}
else {
*magres = frac_make(0, 1);
}
} }
return ar; 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 *dwtype = NULL;
const weapon_type *awtype = NULL; const weapon_type *awtype = NULL;
const weapon *weapon; const weapon *weapon;
double res = 1.0; variant res = frac_make(1, 1);
int rda, sk = 0, sd; int rda, sk = 0, sd;
bool magic = false; bool magic = false;
@ -1190,9 +1200,9 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile)
return false; return false;
} }
/* TODO not sure if res could be > 1 here */
if (magic) { 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) { if (type != AT_COMBATSPELL && type != AT_SPELL) {

View File

@ -248,7 +248,7 @@ extern "C" {
int count_enemies(struct battle *b, const struct fighter *af, int count_enemies(struct battle *b, const struct fighter *af,
int minrow, int maxrow, int select); int minrow, int maxrow, int select);
int natural_armor(struct unit * u); 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 terminate(troop dt, troop at, int type, const char *damage,
bool missile); bool missile);
extern void message_all(battle * b, struct message *m); extern void message_all(battle * b, struct message *m);

View File

@ -250,23 +250,24 @@ static void test_calculate_armor(CuTest * tc)
armor_type *ashield, *achain; armor_type *ashield, *achain;
item_type *ibelt, *ishield, *ichain; item_type *ibelt, *ishield, *ichain;
race *rc; race *rc;
double magres = 0.0; variant magres = frac_zero;
variant v50p = frac_make(1, 2);
test_setup(); test_setup();
r = test_create_region(0, 0, 0); r = test_create_region(0, 0, 0);
ibelt = it_get_or_create(rt_get_or_create("trollbelt")); ibelt = it_get_or_create(rt_get_or_create("trollbelt"));
ishield = it_get_or_create(rt_get_or_create("shield")); 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")); 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);
wtype = new_weapontype(it_get_or_create(rt_get_or_create("sword")), 0, 0.5, 0, 0, 0, 0, SK_MELEE, 1); 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 = test_create_race("human");
du = test_create_unit(test_create_faction(rc), r); du = test_create_unit(test_create_faction(rc), r);
dt.index = 0; dt.index = 0;
dt.fighter = setup_fighter(&b, du); 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, &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); free_battle(b);
b = NULL; b = NULL;
@ -299,13 +300,13 @@ static void test_calculate_armor(CuTest * tc)
wtype->flags = WTF_NONE; 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, &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; ashield->flags |= ATF_LAEN;
achain->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)); 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); free_battle(b);
test_cleanup(); test_cleanup();
} }
@ -319,38 +320,42 @@ static void test_magic_resistance(CuTest *tc)
armor_type *ashield, *achain; armor_type *ashield, *achain;
item_type *ishield, *ichain; item_type *ishield, *ichain;
race *rc; race *rc;
double magres; variant magres;
variant v50p = frac_make(1, 2);
variant v10p = frac_make(1, 10);
test_setup(); test_setup();
r = test_create_region(0, 0, 0); r = test_create_region(0, 0, 0);
ishield = it_get_or_create(rt_get_or_create("shield")); 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")); 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"); rc = test_create_race("human");
du = test_create_unit(test_create_faction(rc), r); du = test_create_unit(test_create_faction(rc), r);
dt.index = 0; dt.index = 0;
i_change(&du->items, ishield, 1);
dt.fighter = setup_fighter(&b, du); dt.fighter = setup_fighter(&b, du);
calculate_armor(dt, 0, 0, &magres); calculate_armor(dt, 0, 0, &magres);
CuAssertDblEquals_Msg(tc, "no magres bonus", 0.0, magic_resistance(du), 0.01); CuAssertIntEquals_Msg(tc, "no magres reduction", magres.sa[1], magres.sa[0]);
CuAssertDblEquals_Msg(tc, "no magres reduction", 1.0, magres, 0.01); magres = magic_resistance(du);
CuAssertIntEquals_Msg(tc, "no magres reduction", 0, magres.sa[0]);
ashield->flags |= ATF_LAEN; ashield->flags |= ATF_LAEN;
ashield->magres = 0.1; ashield->magres = v10p;
calculate_armor(dt, 0, 0, &magres); calculate_armor(dt, 0, 0, &magres);
CuAssert(tc, "laen reduction => 10%%", frac_equal(frac_make(9, 10), magres));
free_battle(b); free_battle(b);
b = NULL; b = NULL;
i_change(&du->items, ishield, 1);
i_change(&du->items, ichain, 1); i_change(&du->items, ichain, 1);
achain->flags |= ATF_LAEN; achain->flags |= ATF_LAEN;
achain->magres = 0.1; achain->magres = v10p;
ashield->flags |= ATF_LAEN; ashield->flags |= ATF_LAEN;
ashield->magres = 0.1; ashield->magres = v10p;
dt.fighter = setup_fighter(&b, du); dt.fighter = setup_fighter(&b, du);
calculate_armor(dt, 0, 0, &magres); 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); free_battle(b);
b = NULL; b = NULL;
@ -359,17 +364,20 @@ static void test_magic_resistance(CuTest *tc)
set_level(du, SK_MAGIC, 2); set_level(du, SK_MAGIC, 2);
dt.fighter = setup_fighter(&b, du); dt.fighter = setup_fighter(&b, du);
calculate_armor(dt, 0, 0, &magres); calculate_armor(dt, 0, 0, &magres);
CuAssertDblEquals_Msg(tc, "skill bonus", 0.1, magic_resistance(du), 0.01); CuAssert(tc, "skill reduction => 90%%", frac_equal(magres, frac_make(9, 10)));
CuAssertDblEquals_Msg(tc, "skill reduction", 0.9, magres, 0.01); magres = magic_resistance(du);
rc->magres = 50; /* percentage, gets added to skill bonus */ CuAssert(tc, "skill reduction", frac_equal(magres, v10p));
rc->magres = v50p; /* percentage, gets added to skill bonus */
calculate_armor(dt, 0, 0, &magres); calculate_armor(dt, 0, 0, &magres);
CuAssertDblEquals_Msg(tc, "race bonus", 0.6, magic_resistance(du), 0.01); CuAssert(tc, "race reduction => 40%%", frac_equal(magres, frac_make(4, 10)));
CuAssertDblEquals_Msg(tc, "race reduction", 0.4, magres, 0.01); 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 */ rc->magres = frac_make(15, 10); /* 150% resistance should not cause negative damage multiplier */
CuAssertDblEquals_Msg(tc, "magic resistance is never > 0.9", 0.9, magic_resistance(du), 0.01); 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); 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); free_battle(b);
test_cleanup(); test_cleanup();
@ -385,14 +393,15 @@ static void test_projectile_armor(CuTest * tc)
armor_type *ashield, *achain; armor_type *ashield, *achain;
item_type *ishield, *ichain; item_type *ishield, *ichain;
race *rc; race *rc;
variant v50p = frac_make(1, 2);
test_setup(); test_setup();
r = test_create_region(0, 0, 0); r = test_create_region(0, 0, 0);
ishield = it_get_or_create(rt_get_or_create("shield")); 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")); 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);
wtype = new_weapontype(it_get_or_create(rt_get_or_create("sword")), 0, 0.5, 0, 0, 0, 0, SK_MELEE, 1); 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 = test_create_race("human");
rc->battle_flags |= BF_EQUIPMENT; rc->battle_flags |= BF_EQUIPMENT;
du = test_create_unit(test_create_faction(rc), r); du = test_create_unit(test_create_faction(rc), r);

View File

@ -211,7 +211,7 @@ static void test_tax_cmd(CuTest *tc) {
silver = get_resourcetype(R_SILVER)->itype; silver = get_resourcetype(R_SILVER)->itype;
sword = it_get_or_create(rt_get_or_create("sword")); 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); i_change(&u->items, sword, 1);
set_level(u, SK_MELEE, 1); set_level(u, SK_MELEE, 1);

View File

@ -42,7 +42,7 @@ static void test_guard_unskilled(CuTest * tc)
test_setup(); test_setup();
itype = it_get_or_create(rt_get_or_create("sword")); 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); r = test_create_region(0, 0, 0);
u = test_create_unit(test_create_faction(0), r); u = test_create_unit(test_create_faction(0), r);
ug = 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(); test_setup();
itype = it_get_or_create(rt_get_or_create("sword")); 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); r = test_create_region(0, 0, 0);
u = test_create_unit(test_create_faction(0), r); u = test_create_unit(test_create_faction(0), r);
ug = 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(); test_setup();
itype = it_get_or_create(rt_get_or_create("sword")); 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); r = test_create_region(0, 0, 0);
ug = test_create_unit(test_create_faction(0), r); ug = test_create_unit(test_create_faction(0), r);
i_change(&ug->items, itype, 1); i_change(&ug->items, itype, 1);

View File

@ -60,7 +60,7 @@ extern "C" {
int capacity; /* Kapazit<69>t pro Gr<47><72>enpunkt */ int capacity; /* Kapazit<69>t pro Gr<47><72>enpunkt */
int maxcapacity; /* Max. Kapazit<69>t */ int maxcapacity; /* Max. Kapazit<69>t */
int maxsize; /* how big can it get, with all the extensions? */ 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 magresbonus; /* bonus it gives the target against spells */
int fumblebonus; /* bonus that reduces fumbling */ int fumblebonus; /* bonus that reduces fumbling */
double auraregen; /* modifier for aura regeneration inside building */ double auraregen; /* modifier for aura regeneration inside building */

View File

@ -381,7 +381,7 @@ static void test_btype_defaults(CuTest *tc) {
CuAssertIntEquals(tc, -1, btype->maxsize); CuAssertIntEquals(tc, -1, btype->maxsize);
CuAssertIntEquals(tc, 1, btype->capacity); CuAssertIntEquals(tc, 1, btype->capacity);
CuAssertIntEquals(tc, -1, btype->maxcapacity); 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->magresbonus);
CuAssertIntEquals(tc, 0, btype->fumblebonus); CuAssertIntEquals(tc, 0, btype->fumblebonus);
CuAssertIntEquals(tc, 0, btype->flags); CuAssertIntEquals(tc, 0, btype->flags);

View File

@ -275,7 +275,7 @@ luxury_type *new_luxurytype(item_type * itype, int price)
} }
weapon_type *new_weapontype(item_type * itype, 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) int reload, skill_t sk, int minskill)
{ {
weapon_type *wtype; weapon_type *wtype;
@ -301,7 +301,7 @@ weapon_type *new_weapontype(item_type * itype,
return wtype; 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) int prot, unsigned int flags)
{ {
armor_type *atype; armor_type *atype;

View File

@ -181,7 +181,7 @@ extern "C" {
const item_type *itype; const item_type *itype;
unsigned int flags; unsigned int flags;
double penalty; double penalty;
double magres; variant magres;
int prot; int prot;
float projectile; /* chance, dass ein projektil abprallt */ float projectile; /* chance, dass ein projektil abprallt */
} armor_type; } armor_type;
@ -205,7 +205,7 @@ extern "C" {
int minskill; int minskill;
int offmod; int offmod;
int defmod; int defmod;
double magres; variant magres;
int reload; /* time to reload this weapon */ int reload; /* time to reload this weapon */
weapon_mod *modifiers; weapon_mod *modifiers;
/* --- functions --- */ /* --- functions --- */
@ -244,14 +244,12 @@ extern "C" {
/* creation */ /* creation */
resource_type *rt_get_or_create(const char *name); resource_type *rt_get_or_create(const char *name);
item_type *it_get_or_create(resource_type *rtype); 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); luxury_type *new_luxurytype(item_type * itype, int price);
weapon_type *new_weapontype(item_type * itype, int wflags, 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); skill_t sk, int minskill);
armor_type *new_armortype(item_type * itype, double penalty, 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); potion_type *new_potiontype(item_type * itype, int level);
typedef enum { typedef enum {

View File

@ -460,7 +460,7 @@ static void json_race(cJSON *json, race *rc) {
break; break;
case cJSON_Number: case cJSON_Number:
if (strcmp(child->string, "magres") == 0) { if (strcmp(child->string, "magres") == 0) {
rc->magres = child->valueint; rc->magres = frac_make(child->valueint, 100);
} }
else if (strcmp(child->string, "maxaura") == 0) { else if (strcmp(child->string, "maxaura") == 0) {
rc->maxaura = child->valueint; rc->maxaura = child->valueint;

View File

@ -163,8 +163,7 @@ static void test_races(CuTest * tc)
CuAssertPtrNotNull(tc, rc); CuAssertPtrNotNull(tc, rc);
CuAssertIntEquals(tc, RCF_NPC | RCF_WALK | RCF_UNDEAD, rc->flags); CuAssertIntEquals(tc, RCF_NPC | RCF_WALK | RCF_UNDEAD, rc->flags);
CuAssertStrEquals(tc, "1d4", rc->def_damage); CuAssertStrEquals(tc, "1d4", rc->def_damage);
CuAssertIntEquals(tc, 100, rc->magres); CuAssertTrue(tc, frac_equal(frac_one, rc->magres));
CuAssertDblEquals(tc, 1.0, rc_magres(rc), 0.0);
CuAssertIntEquals(tc, 200, rc->maxaura); CuAssertIntEquals(tc, 200, rc->maxaura);
CuAssertDblEquals(tc, 2.0, rc_maxaura(rc), 0.0); CuAssertDblEquals(tc, 2.0, rc_maxaura(rc), 0.0);
CuAssertDblEquals(tc, 3.0, rc->regaura, 0.0); CuAssertDblEquals(tc, 3.0, rc->regaura, 0.0);

View File

@ -331,6 +331,7 @@ race *rc_create(const char *zName)
assert(zName); assert(zName);
rc = (race *)calloc(sizeof(race), 1); rc = (race *)calloc(sizeof(race), 1);
rc->magres.sa[1] = 1;
rc->hitpoints = 1; rc->hitpoints = 1;
rc->weight = PERSON_WEIGHT; rc->weight = PERSON_WEIGHT;
rc->capacity = 540; rc->capacity = 540;
@ -383,8 +384,8 @@ bool r_insectstalled(const region * r)
return fval(r->terrain, ARCTIC_REGION); return fval(r->terrain, ARCTIC_REGION);
} }
double rc_magres(const race *rc) { variant rc_magres(const race *rc) {
return rc->magres / 100.0; return rc->magres;
} }
double rc_maxaura(const race *rc) { double rc_maxaura(const race *rc) {

View File

@ -116,7 +116,7 @@ extern "C" {
typedef struct race { typedef struct race {
char *_name; char *_name;
int magres; variant magres;
int healing; int healing;
int maxaura; /* Faktor auf Maximale Aura */ int maxaura; /* Faktor auf Maximale Aura */
double regaura; /* Faktor auf Regeneration */ double regaura; /* Faktor auf Regeneration */
@ -182,7 +182,7 @@ extern "C" {
int rc_luxury_trade(const struct race *rc); int rc_luxury_trade(const struct race *rc);
int rc_herb_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); double rc_maxaura(const struct race *rc);
int rc_armor_bonus(const struct race *rc); int rc_armor_bonus(const struct race *rc);
int rc_scare(const struct race *rc); int rc_scare(const struct race *rc);

View File

@ -24,8 +24,7 @@ static void test_rc_defaults(CuTest *tc) {
rc = rc_get_or_create("human"); rc = rc_get_or_create("human");
CuAssertStrEquals(tc, "human", rc->_name); CuAssertStrEquals(tc, "human", rc->_name);
CuAssertIntEquals(tc, 0, rc_armor_bonus(rc)); CuAssertIntEquals(tc, 0, rc_armor_bonus(rc));
CuAssertIntEquals(tc, 0, rc->magres); CuAssertIntEquals(tc, 0, rc->magres.sa[0]);
CuAssertDblEquals(tc, 0.0, rc_magres(rc), 0.0);
CuAssertIntEquals(tc, 0, rc->healing); CuAssertIntEquals(tc, 0, rc->healing);
CuAssertDblEquals(tc, 0.0, rc_maxaura(rc), 0.0); CuAssertDblEquals(tc, 0.0, rc_maxaura(rc), 0.0);
CuAssertDblEquals(tc, 1.0, rc->recruit_multi, 0.0); CuAssertDblEquals(tc, 1.0, rc->recruit_multi, 0.0);

View File

@ -61,6 +61,19 @@ without prior permission by the authors of Eressea.
#include <string.h> #include <string.h>
#ifdef USE_LIBXML2 #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) static void xml_readtext(xmlNodePtr node, struct locale **lang, xmlChar ** text)
{ {
xmlChar *propValue = xmlGetProp(node, BAD_CAST "locale"); 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->maxcapacity = xml_ivalue(node, "maxcapacity", btype->maxcapacity);
btype->maxsize = xml_ivalue(node, "maxsize", btype->maxsize); 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->magresbonus = xml_ivalue(node, "magresbonus", btype->magresbonus);
btype->fumblebonus = xml_ivalue(node, "fumblebonus", btype->fumblebonus); btype->fumblebonus = xml_ivalue(node, "fumblebonus", btype->fumblebonus);
btype->auraregen = xml_fvalue(node, "auraregen", btype->auraregen); 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; unsigned int flags = ATF_NONE;
int ac = xml_ivalue(node, "ac", 0); int ac = xml_ivalue(node, "ac", 0);
double penalty = xml_fvalue(node, "penalty", 0.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)) if (xml_bvalue(node, "laen", false))
flags |= ATF_LAEN; 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 offmod = xml_ivalue(node, "offmod", 0);
int defmod = xml_ivalue(node, "defmod", 0); int defmod = xml_ivalue(node, "defmod", 0);
int reload = xml_ivalue(node, "reload", 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)) if (xml_bvalue(node, "armorpiercing", false))
flags |= WTF_ARMORPIERCING; flags |= WTF_ARMORPIERCING;
@ -899,22 +912,6 @@ static int parse_rules(xmlDocPtr doc)
return 0; 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) static int parse_resources(xmlDocPtr doc)
{ {
xmlXPathContextPtr xpath = xmlXPathNewContext(doc); xmlXPathContextPtr xpath = xmlXPathNewContext(doc);
@ -1031,15 +1028,7 @@ static int parse_resources(xmlDocPtr doc)
rdata->modifiers[k].flags = RMF_SKILL; rdata->modifiers[k].flags = RMF_SKILL;
} }
else if (strcmp((const char *)propValue, "material") == 0) { else if (strcmp((const char *)propValue, "material") == 0) {
int g, num, den = 100; rdata->modifiers[k].value = xml_fraction(node, "value");
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].flags = RMF_SAVEMATERIAL; rdata->modifiers[k].flags = RMF_SAVEMATERIAL;
} }
else if (strcmp((const char *)propValue, "require") == 0) { 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); rc->def_damage = strdup((const char *)propValue);
xmlFree(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->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->maxaura = (int)(xml_fvalue(node, "maxaura", rc->maxaura) * 100); /* TODO: store as int in XML */
rc->regaura = (float)xml_fvalue(node, "regaura", rc->regaura); rc->regaura = (float)xml_fvalue(node, "regaura", rc->regaura);

View File

@ -611,7 +611,7 @@ void setup_guard(guard_fixture *fix, bool armed) {
if (armed) { if (armed) {
item_type *itype; item_type *itype;
itype = it_get_or_create(rt_get_or_create("sword")); 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); i_change(&u->items, itype, 1);
set_level(u, SK_MELEE, 2); set_level(u, SK_MELEE, 2);
} }
@ -1481,7 +1481,7 @@ static void test_armedmen(CuTest *tc) {
test_setup(); test_setup();
u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0));
it_sword = test_create_itemtype("sword"); 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, false));
CuAssertIntEquals(tc, 0, armedmen(u, true)); CuAssertIntEquals(tc, 0, armedmen(u, true));
set_level(u, SK_MELEE, 1); set_level(u, SK_MELEE, 1);

View File

@ -1135,27 +1135,29 @@ static int farcasting(unit * magician, region * r)
/* allgemeine Magieresistenz einer Einheit, /* allgemeine Magieresistenz einer Einheit,
* reduziert magischen Schaden */ * reduziert magischen Schaden */
double magic_resistance(unit * target) variant magic_resistance(unit * target)
{ {
attrib *a; attrib *a;
curse *c; curse *c;
const curse_type * ct_goodresist = 0, *ct_badresist = 0; const curse_type * ct_goodresist = 0, *ct_badresist = 0;
const resource_type *rtype; const resource_type *rtype;
const race *rc = u_race(target); const race *rc = u_race(target);
double probability = rc_magres(rc); variant v, prob = rc_magres(rc);
const plane *pl = rplane(target->region); const plane *pl = rplane(target->region);
if (rc == get_race(RC_HIRNTOETER) && !pl) { if (rc == get_race(RC_HIRNTOETER) && !pl) {
probability /= 2; prob.sa[1] *= 2;
} }
assert(target->number > 0); assert(target->number > 0);
/* Magier haben einen Resistenzbonus vom Magietalent * 5% */ /* 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 */ /* Auswirkungen von Zaubern auf der Einheit */
c = get_curse(target->attribs, ct_find("magicresistance")); c = get_curse(target->attribs, ct_find("magicresistance"));
if (c) { 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 */ /* Unicorn +10 */
@ -1163,7 +1165,7 @@ double magic_resistance(unit * target)
if (rtype) { if (rtype) {
int n = i_get(target->items, rtype->itype); int n = i_get(target->items, rtype->itype);
if (n) { 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 (mage != NULL) {
if (ct_goodresist && c->type == ct_goodresist) { if (ct_goodresist && c->type == ct_goodresist) {
if (alliedunit(mage, target->faction, HELP_GUARD)) { 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 */ ct_goodresist = 0; /* only one effect per region */
} }
} }
else if (ct_badresist && c->type == ct_badresist) { else if (ct_badresist && c->type == ct_badresist) {
if (!alliedunit(mage, target->faction, HELP_GUARD)) { 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 */ 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; const struct building_type *btype = building_is_active(b) ? b->type : NULL;
/* gesegneter Steinkreis gibt 30% dazu */ /* gesegneter Steinkreis gibt 30% dazu */
if (btype) if (btype) {
probability += btype->magresbonus * 0.01; /* 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 bool
target_resists_magic(unit * magician, void *obj, int objtyp, int t_bonus) 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) if (magician == NULL || obj == NULL) {
return true;
if (obj == NULL)
return true; return true;
}
switch (objtyp) { switch (objtyp) {
case TYP_UNIT: case TYP_UNIT:
@ -1248,43 +1259,56 @@ target_resists_magic(unit * magician, void *obj, int objtyp, int t_bonus)
pa = sk; pa = sk;
} }
/* Contest */ /* Contest, probability = 0.05 * (10 + pa - at); */
probability = 0.05 * (10 + pa - at); prob = frac_add(prob, frac_make(10 + pa - at, 20));
prob = frac_add(prob, magic_resistance((unit *)obj));
probability += magic_resistance((unit *)obj);
break; break;
} }
case TYP_REGION: case TYP_REGION:
/* Bonus durch Zauber */ /* Bonus durch Zauber
probability += 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; break;
case TYP_BUILDING: case TYP_BUILDING:
/* Bonus durch Zauber */ /* Bonus durch Zauber
probability += probability +=
0.01 * get_curseeffect(((building *)obj)->attribs, C_RESIST_MAGIC, 0); 0.01 * get_curseeffect(((building *)obj)->attribs, C_RESIST_MAGIC, 0); */
a = ((building *)obj)->attribs;
/* Bonus durch Typ */ /* Bonus durch Typ
probability += 0.01 * ((building *)obj)->type->magres; probability += 0.01 * ((building *)obj)->type->magres; */
prob = frac_add(prob, ((building *)obj)->type->magres);
break; break;
case TYP_SHIP: case TYP_SHIP:
/* Bonus durch Zauber */ /* Bonus durch Zauber */
probability += a = ((ship *)obj)->attribs;
0.01 * get_curseeffect(((ship *)obj)->attribs, C_RESIST_MAGIC, 0);
break; break;
} }
probability = MAX(0.02, probability + t_bonus * 0.01); if (a) {
probability = MIN(0.98, probability); 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 /* gibt true, wenn die Zufallszahl kleiner als die chance ist und
* false, wenn sie gleich oder größer ist, dh je größer die * false, wenn sie gleich oder größer ist, dh je größer die
* Magieresistenz (chance) desto eher gibt die Funktion true zurück */ * Magieresistenz (chance) desto eher gibt die Funktion true zurück */
return chance(probability); return rng_int() % prob.sa[1] < prob.sa[0];
} }
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */

View File

@ -315,7 +315,7 @@ extern "C" {
bool is_magic_resistant(struct unit *magician, struct unit *target, int bool is_magic_resistant(struct unit *magician, struct unit *target, int
resist_bonus); resist_bonus);
/* Mapperfunktion für target_resists_magic() vom Typ struct unit. */ /* 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 /* gibt die Chance an, mit der einem Zauber widerstanden wird. Je
* größer, desto resistenter ist da Opfer */ * größer, desto resistenter ist da Opfer */
bool target_resists_magic(struct unit *magician, void *obj, int objtyp, bool target_resists_magic(struct unit *magician, void *obj, int objtyp,

View File

@ -414,15 +414,15 @@ static void test_magic_resistance(CuTest *tc) {
test_setup(); test_setup();
rc = test_create_race("human"); rc = test_create_race("human");
u = test_create_unit(test_create_faction(rc), test_create_region(0, 0, 0)); 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); CuAssertTrue(tc, frac_equal(rc->magres, magic_resistance(u)));
rc->magres = 100; rc->magres = frac_one;
CuAssertDblEquals_Msg(tc, "magic resistance is capped at 0.9", 0.9, magic_resistance(u), 0.01); CuAssert(tc, "magic resistance is capped at 0.9", frac_equal(magic_resistance(u), frac_make(9, 10)));
rc = test_create_race("braineater"); rc = test_create_race("braineater");
rc->magres = 100; rc->magres = frac_one;
u_setrace(u, rc); 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(); 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(); test_cleanup();
} }

View File

@ -2851,7 +2851,8 @@ static int dc_age(struct curse *c)
while (*up != NULL) { while (*up != NULL) {
unit *u = *up; unit *u = *up;
int hp; 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)) { if (u->number <= 0 || target_resists_magic(mage, u, TYP_UNIT, 0)) {
up = &u->next; up = &u->next;
@ -2859,7 +2860,9 @@ static int dc_age(struct curse *c)
} }
/* Reduziert durch Magieresistenz */ /* 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); hp = change_hitpoints(u, -(int)damage);
ADDMSG(&u->faction->msgs, msg_message((hp>0)?"poison_damage":"poison_death", "region unit", r, u)); ADDMSG(&u->faction->msgs, msg_message((hp>0)?"poison_damage":"poison_death", "region unit", r, u));

View File

@ -71,7 +71,7 @@ static void test_all_spy_message(CuTest *tc) {
set_factionstealth(fix.victim, fix.spy->faction); set_factionstealth(fix.victim, fix.spy->faction);
itype = it_get_or_create(rt_get_or_create("sword")); 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); i_change(&fix.victim->items, itype, 1);
spy_message(99, fix.spy, fix.victim); spy_message(99, fix.spy, fix.victim);

View File

@ -80,7 +80,6 @@ damage_unit(unit * u, const char *dam, bool physical, bool magic)
int *hp, hpstack[20]; int *hp, hpstack[20];
int h; int h;
int i, dead = 0, hp_rem = 0, heiltrank; int i, dead = 0, hp_rem = 0, heiltrank;
double magres = magic_resistance(u);
assert(u->number); assert(u->number);
if (fval(u_race(u), RCF_ILLUSIONARY) || u_race(u) == get_race(RC_SPELL)) { 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 */ /* Schaden */
for (i = 0; i < u->number; i++) { for (i = 0; i < u->number; i++) {
int damage = dice_rand(dam); int damage = dice_rand(dam);
if (magic) if (magic) {
damage = (int)(damage * (1.0 - magres)); variant magres = magic_resistance(u);
if (physical) magres = frac_sub(frac_make(1, 1), magres);
damage = damage * magres.sa[0] / magres.sa[1];
}
if (physical) {
damage -= nb_armor(u, i); damage -= nb_armor(u, i);
}
hp[i] -= damage; hp[i] -= damage;
} }