forked from github/server
add tests for magic resistance formula.
This commit is contained in:
parent
8ba80d7dbf
commit
9a9a9c7a5d
5 changed files with 151 additions and 76 deletions
|
@ -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) {
|
||||
|
|
|
@ -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 *);
|
||||
|
|
143
src/magic.c
143
src/magic.c
|
@ -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);
|
||||
|
|
12
src/magic.h
12
src/magic.h
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue