add tests for magic resistance formula.
This commit is contained in:
Enno Rehling 2021-03-27 00:01:44 +01:00
parent 8ba80d7dbf
commit 9a9a9c7a5d
5 changed files with 151 additions and 76 deletions

View File

@ -330,9 +330,9 @@ static bool cmp_curse(const attrib * a, const void *data)
return false;
}
curse *get_curse(attrib * ap, const curse_type * ctype)
curse *get_curse(const attrib * ap, const curse_type * ctype)
{
attrib *a = ap;
const attrib *a = ap;
if (!ctype) return NULL;
while (a) {
if (a->type == &at_curse) {

View File

@ -264,7 +264,7 @@ extern "C" {
/* gibt pointer auf die erste curse-struct zurueck, deren Typ ctype ist,
* oder einen NULL-pointer
* */
struct curse *get_curse(struct attrib *ap, const curse_type * ctype);
struct curse *get_curse(const struct attrib *ap, const curse_type * ctype);
const curse_type *ct_find(const char *c);
void ct_register(const curse_type *);

View File

@ -1048,7 +1048,7 @@ static int farcasting(unit * magician, region * r)
/* allgemeine Magieresistenz einer Einheit,
* reduziert magischen Schaden */
variant magic_resistance(unit * target)
variant magic_resistance(const unit * target)
{
attrib *a;
curse *c;
@ -1132,6 +1132,74 @@ variant magic_resistance(unit * target)
return prob;
}
variant resist_chance(const unit *magician, const void *obj, int objtyp, int bonus_percent) {
variant v02p, v98p, prob = frac_make(bonus_percent, 100);
const attrib *a = NULL;
switch (objtyp) {
case TYP_UNIT:
{
int at, pa = 0;
const skill *sv;
const unit *u = (const unit *)obj;
if (ucontact(u, magician)) {
return frac_zero;
}
at = effskill(magician, SK_MAGIC, NULL);
for (sv = u->skills; sv != u->skills + u->skill_size; ++sv) {
int sk = eff_skill(u, sv, NULL);
if (pa < sk)
pa = sk;
}
/* Contest, probability = 0.05 * (10 + pa - at); */
prob = frac_add(prob, frac_make(10 + pa - at, 20));
prob = frac_add(prob, magic_resistance(u));
break;
}
case TYP_REGION:
/* Bonus durch Zauber
probability +=
0.01 * get_curseeffect(((region *)obj)->attribs, C_RESIST_MAGIC, 0); */
a = ((const region *)obj)->attribs;
break;
case TYP_BUILDING:
/* Bonus durch Zauber
probability +=
0.01 * get_curseeffect(((building *)obj)->attribs, C_RESIST_MAGIC, 0); */
a = ((const building *)obj)->attribs;
/* Bonus durch Typ
probability += 0.01 * ((building *)obj)->type->magres; */
prob = frac_add(prob, ((const building *)obj)->type->magres);
break;
case TYP_SHIP:
/* Bonus durch Zauber */
a = ((const ship *)obj)->attribs;
break;
}
if (a) {
const curse *c = get_curse(a, &ct_magicrunes);
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) {
return v02p;
}
else if (frac_sign(frac_sub(prob, v98p)) > 0) {
return v98p;
}
return prob;
}
/* ------------------------------------------------------------- */
/* Prueft, ob das Objekt dem Zauber widerstehen kann.
* Objekte koennen Regionen, Units, Gebaeude oder Schiffe sein.
@ -1144,10 +1212,9 @@ variant magic_resistance(unit * target)
*/
bool
target_resists_magic(unit * magician, void *obj, int objtyp, int t_bonus)
target_resists_magic(const unit * magician, const void *obj, int objtyp, int t_bonus)
{
variant v02p, v98p, prob = frac_make(t_bonus, 100);
attrib *a = NULL;
variant prob;
static bool never_resist;
static int config;
if (config_changed(&config)) {
@ -1161,78 +1228,20 @@ target_resists_magic(unit * magician, void *obj, int objtyp, int t_bonus)
return true;
}
switch (objtyp) {
case TYP_UNIT:
{
int at, pa = 0;
skill *sv;
unit *u = (unit *)obj;
if (ucontact(u, magician)) {
return false;
}
at = effskill(magician, SK_MAGIC, NULL);
for (sv = u->skills; sv != u->skills + u->skill_size; ++sv) {
int sk = eff_skill(u, sv, NULL);
if (pa < sk)
pa = sk;
}
/* 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
probability +=
0.01 * get_curseeffect(((region *)obj)->attribs, C_RESIST_MAGIC, 0); */
a = ((region *)obj)->attribs;
break;
case TYP_BUILDING:
/* Bonus durch Zauber
probability +=
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 */
a = ((ship *)obj)->attribs;
break;
}
if (a) {
curse * c = get_curse(a, &ct_magicrunes);
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;
}
prob = resist_chance(magician, obj, objtyp, t_bonus);
/* gibt true, wenn die Zufallszahl kleiner als die chance ist und
* false, wenn sie gleich oder groesser ist, dh je groesser die
* Magieresistenz (chance) desto eher gibt die Funktion true zurueck */
if (prob.sa[0] <= 0) {
return false;
}
return rng_int() % prob.sa[1] < prob.sa[0];
}
/* ------------------------------------------------------------- */
bool is_magic_resistant(unit * magician, unit * target, int resist_bonus)
bool is_magic_resistant(const unit * magician, const unit * target, int resist_bonus)
{
return target_resists_magic(magician, target, TYP_UNIT,
resist_bonus);

View File

@ -287,14 +287,16 @@ extern "C" {
/* ermittelt die effektive Stufe des Zaubers. Dabei ist cast_level
* die gewuenschte maximale Stufe (im Normalfall Stufe des Magiers,
* bei Farcasting Stufe*2^Entfernung) */
bool is_magic_resistant(struct unit *magician, struct unit *target, int
resist_bonus);
bool is_magic_resistant(const struct unit *magician, const struct unit *target,
int resist_bonus);
/* Mapperfunktion fuer target_resists_magic() vom Typ struct unit. */
variant magic_resistance(struct unit *target);
variant magic_resistance(const struct unit *target);
/* gibt die Chance an, mit der einem Zauber widerstanden wird. Je
* groesser, desto resistenter ist da Opfer */
bool target_resists_magic(struct unit *magician, void *obj, int objtyp,
int resist_bonus);
bool target_resists_magic(const struct unit *magician, const void *obj, int objtyp,
int bonus_percent);
variant resist_chance(const struct unit *magician, const void *obj, int objtyp,
int bonus_percent);
/* gibt false zurueck, wenn der Zauber gelingt, true, wenn das Ziel
* widersteht */
struct spell * unit_getspell(struct unit *u, const char *s,

View File

@ -1,8 +1,10 @@
#include <platform.h>
#include "magic.h"
#include "teleport.h"
#include "contact.h"
#include "give.h"
#include "teleport.h"
#include <kernel/callbacks.h>
#include <kernel/building.h>
@ -15,6 +17,7 @@
#include <kernel/spell.h>
#include <kernel/spellbook.h>
#include <kernel/unit.h>
#include <kernel/objtypes.h>
#include <kernel/pool.h>
#include <kernel/attrib.h>
@ -420,6 +423,66 @@ void test_multi_cast(CuTest *tc) {
test_teardown();
}
static void test_resist_chance(CuTest *tc) {
unit *u1, *u2, *u3, *u4;
variant prob;
test_setup();
u1 = test_create_unit(test_create_faction(), test_create_plain(0, 0));
u2 = test_create_unit(test_create_faction(), u1->region);
u3 = test_create_unit(test_create_faction(), u1->region);
u4 = test_create_unit(u1->faction, u1->region);
/* my own units never resist: */
prob = resist_chance(u1, u4, TYP_UNIT, 0);
CuAssertIntEquals(tc, 0, prob.sa[0]);
prob = resist_chance(u1, u4, TYP_UNIT, 10);
CuAssertIntEquals(tc, 0, prob.sa[0]);
/* allied units do not resist */
contact_unit(u3, u1);
prob = resist_chance(u1, u3, TYP_UNIT, 0);
CuAssertIntEquals(tc, 0, prob.sa[0]);
prob = resist_chance(u1, u3, TYP_UNIT, 10);
CuAssertIntEquals(tc, 0, prob.sa[0]);
/* 50% base chance for equal skills */
prob = resist_chance(u1, u2, TYP_UNIT, 0);
prob = frac_sub(prob, frac_make(1, 2));
CuAssertIntEquals(tc, 0, prob.sa[0]);
/* 8 levels better = 40% reduced */
set_level(u1, SK_MAGIC, 8);
prob = resist_chance(u1, u2, TYP_UNIT, 0);
prob = frac_sub(prob, frac_make(1, 10));
CuAssertIntEquals(tc, 0, prob.sa[0]);
/* apply a 10% bonus */
prob = resist_chance(u1, u2, TYP_UNIT, 10);
prob = frac_sub(prob, frac_make(1, 5));
CuAssertIntEquals(tc, 0, prob.sa[0]);
/* never less than 2 percent */
set_level(u1, SK_MAGIC, 10); /* negates the 50% base chance */
prob = resist_chance(u1, u2, TYP_UNIT, 0);
prob = frac_sub(prob, frac_make(1, 50));
CuAssertIntEquals(tc, 0, prob.sa[0]);
/* never more than 98 percent */
prob = resist_chance(u1, u2, TYP_UNIT, 100);
prob = frac_sub(prob, frac_make(98, 100));
CuAssertIntEquals(tc, 0, prob.sa[0]);
/* 5% bonus per best skill of defender */
set_level(u1, SK_MAGIC, 10);
set_level(u2, SK_ENTERTAINMENT, 10); /* negate magic skill */
prob = resist_chance(u1, u2, TYP_UNIT, 0);
prob = frac_sub(prob, frac_make(1, 2)); /* only 50% base chance remains */
CuAssertIntEquals(tc, 0, prob.sa[0]);
test_teardown();
}
static void test_magic_resistance(CuTest *tc) {
unit *u;
race *rc;
@ -758,6 +821,7 @@ CuSuite *get_magic_suite(void)
SUITE_ADD_TEST(suite, test_set_post_combatspell);
SUITE_ADD_TEST(suite, test_hasspell);
SUITE_ADD_TEST(suite, test_magic_resistance);
SUITE_ADD_TEST(suite, test_resist_chance);
SUITE_ADD_TEST(suite, test_max_spellpoints);
SUITE_ADD_TEST(suite, test_illusioncastle);
SUITE_ADD_TEST(suite, test_regenerate_aura);