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;
}
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) {

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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 */

View File

@ -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);

View File

@ -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;

View File

@ -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 {

View File

@ -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;

View File

@ -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);

View File

@ -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) {

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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];
}
/* ------------------------------------------------------------- */

View File

@ -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,

View File

@ -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();
}

View File

@ -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));

View File

@ -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);

View File

@ -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;
}