finalize spell cost bugfix.
fix familiar spellcost calculation tests.
remove debug names from tests.
This commit is contained in:
Enno Rehling 2021-04-08 21:05:49 +02:00
parent c8e975333b
commit 250c098bf6
8 changed files with 175 additions and 89 deletions

View File

@ -136,11 +136,9 @@ function test_fleeing_units_can_be_transported()
u1.number = 100
u1:add_order("ATTACKIEREN " .. itoa36(u2.id))
u2.number = 100
u2.name = 'Passagier'
u2:add_order("FAHREN " .. itoa36(u3.id))
u2:add_order("KAEMPFE FLIEHE")
u3.number = 100
u3.name = 'Transporter'
u3:add_order("KAEMPFE FLIEHE")
u3:add_order("TRANSPORT " .. itoa36(u2.id))
u3:add_order("NACH O ")
@ -642,7 +640,6 @@ function test_laen2()
u1:set_skill("mining", 15)
u1:clear_orders()
u1:add_order("MACHEN Laen")
u1.name = "Laenmeister"
local b = building.create(r, "mine")
b.size = 10

View File

@ -115,7 +115,6 @@ function test_dwarf_bonus()
local u = unit.create(faction.create("dwarf"), r)
assert_equal("dwarf", u.faction.race)
assert_equal("dwarf", u.race)
u.faction.name = "Zwerge"
u.number = 10
u:set_skill("mining", 1)
u:add_order("MACHE EISEN")

View File

@ -0,0 +1,84 @@
local tcname = 'tests.e2.familiars'
local lunit = require('lunit')
if _VERSION >= 'Lua 5.2' then
_ENV = module(tcname, 'seeall')
else
module(tcname, lunit.testcase, package.seeall)
end
function setup()
eressea.game.reset()
eressea.settings.set("nmr.removenewbie", "0")
eressea.settings.set("nmr.timeout", "0")
eressea.settings.set("NewbieImmunity", "0")
eressea.settings.set("rules.food.flags", "4")
eressea.settings.set("rules.peasants.growth.factor", "0")
eressea.settings.set("magic.resist.enable", "0")
eressea.settings.set("magic.fumble.enable", "0")
eressea.settings.set("magic.regeneration.enable", "0")
end
local function setup_familiars(f, r)
f.magic = 'gwyrrd'
local uf = unit.create(f, r)
uf.magic = 'gray'
local u = unit.create(f, r)
u.magic = 'gwyrrd'
u:set_skill('magic', 9)
u.familiar = uf
return u, uf
end
function test_moneyspell()
local r = region.create(0, 0, "plain")
local f = faction.create('human')
local um, uf = setup_familiars(f, r)
um.aura = 9
um:add_order('ZAUBERE STUFE 9 Viehheilung')
process_orders()
assert_equal(0, um.aura)
assert_equal(450, um:get_item('money'))
end
function test_moneyspell_through_familiar()
-- casting magician's spell with the familiar: double cost
local r = region.create(0, 0, "plain")
local f = faction.create('human')
local um, uf = setup_familiars(f, r)
um.aura = 12
uf:add_order('ZAUBERE STUFE 3 Viehheilung')
process_orders()
assert_equal(0, uf:get_skill('magic'))
assert_equal(12, um.aura) -- cannot cast, familiar needs magic skill
assert_equal(0, uf:get_item('money'))
assert_equal(0, um:get_item('money'))
uf:set_skill('magic', 2) -- can cast no higher than level 2
process_orders()
assert_equal(8, um.aura) -- double the cost
assert_equal(100, uf:get_item('money'))
assert_equal(0, um:get_item('money'))
um:set_skill('magic', 4) -- use at most half of skill
uf:set_skill('magic', 1) -- too low for level 2 spell, cast at level 1
process_orders()
assert_equal(6, um.aura) -- double cost of level 1
assert_equal(150, uf:get_item('money'))
assert_equal(0, um:get_item('money'))
end
function test_moneyspell_as_familiar()
-- familiar has the spell and has magic skills: regular spellcasting rules apply
local r = region.create(0, 0, "plain")
local f = faction.create('human')
local um, uf = setup_familiars(f, r)
um.aura = 9
uf.aura = 20
uf:set_skill('magic', 10)
uf:add_spell('earn_silver#gwyrrd')
uf:add_order('ZAUBERE STUFE 10 Viehheilung')
process_orders()
assert_equal(500, uf:get_item('money'))
assert_equal(10, uf.aura)
assert_equal(9, um.aura)
end

View File

@ -22,7 +22,6 @@ function test_ship_requires_skill()
assert_not_nil(r2)
local f = faction.create("human", "fake@eressea.de", "de")
local u1 = unit.create(f, r1, 1)
u1.name = "fake"
u1.ship = ship.create(r1, "longboat")
u1:clear_orders()
u1:add_order("NACH O")

View File

@ -45,7 +45,6 @@ function test_stealth_faction_on()
end
function test_stealth_faction_other()
u.name = "Enno"
u:clear_orders()
u:add_order("TARNEN PARTEI NUMMER " .. itoa36(f.id))

View File

@ -256,7 +256,6 @@ function test_promote_after_recruit()
local r1 = region.create(0, 0, 'plain')
local r2 = region.create(1, 0, 'plain')
local u1 = unit.create(f, r1, 1)
u1.name = 'Xolgrim'
local u2 = unit.create(f, r2, 55)
u2:add_order('REKRUTIERE 1')
u1:add_order('BEFOERDERE')

View File

@ -267,16 +267,12 @@ bool FactionSpells(void)
return rule != 0;
}
int mage_get_spell_level(const sc_mage *mage, const spell *sp) {
spellbook *book = mage_get_spellbook(mage);
spellbook_entry *sbe = spellbook_get(book, sp);
return sbe ? sbe->level : 0;
}
int get_spell_level_mage(const spell * sp, void * cbdata)
{
sc_mage *mage = (sc_mage *)cbdata;
return mage_get_spell_level(mage, sp);
spellbook *book = mage_get_spellbook(mage);
spellbook_entry *sbe = spellbook_get(book, sp);
return sbe ? sbe->level : 0;
}
/* ------------------------------------------------------------- */
@ -502,12 +498,21 @@ sc_mage *create_mage(unit * u, magic_t mtyp)
/* ------------------------------------------------------------- */
/* Funktionen fuer die Bearbeitung der List-of-known-spells */
int unit_spell_level(const unit *u, const struct spell *sp)
{
spellbook *book = unit_get_spellbook(u);
spellbook_entry *sbe = book ? spellbook_get(book, sp) : NULL;
if (sbe) {
return sbe->level;
}
return 0;
}
bool u_hasspell(const unit *u, const struct spell *sp)
{
spellbook * book = unit_get_spellbook(u);
spellbook_entry * sbe = book ? spellbook_get(book, sp) : NULL;
if (sbe) {
return sbe->level <= effskill(u, SK_MAGIC, NULL);
int level = unit_spell_level(u, sp);
if (level > 0) {
return level <= effskill(u, SK_MAGIC, NULL);
}
return false;
}
@ -826,10 +831,9 @@ void pay_spell(unit * mage, const unit *caster, const spell * sp, int cast_level
*/
bool knowsspell(const region * r, const unit * u, const spell * sp)
{
UNUSED_ARG(r);
assert(sp);
int level = unit_spell_level(u, sp);
/* steht der Spruch in der Spruchliste? */
return u_hasspell(u, sp);
return level > 0;
}
/* Um einen Spruch zu beherrschen, muss der Magier die Stufe des
@ -2479,16 +2483,16 @@ static bool is_moving_ship(ship * sh)
return false;
}
static int default_spell_level(const sc_mage *mage, const spell *sp) {
static int default_spell_level(const unit *u, const spell *sp) {
if (sp && sp->components) {
const struct spell_component *spc;
for (spc = sp->components; spc->type; ++spc) {
if (spc->cost != SPC_FIX) {
return -1;
return 0;
}
}
}
return mage_get_spell_level(mage, sp);
return unit_spell_level(u, sp);
}
#define MAX_PARAMETERS 48
@ -2503,7 +2507,7 @@ static castorder *cast_cmd(unit * u, order * ord)
spell *sp = NULL;
plane *pl;
spellparameter *args = NULL;
unit * mage = u;
unit * mage = NULL;
param_t param;
if (LongHunger(u)) {
@ -2553,40 +2557,50 @@ static castorder *cast_cmd(unit * u, order * ord)
return 0;
}
/**
* skill = der maximale durch das Magietalent erlaubte Level.
* Entspricht dem Talent des Zaubernden, oder im Falle, dass ein
* Vertrauter einen Spruch seines Magiers zaubert, dessen halbes Talent.
*/
skill = effskill(u, SK_MAGIC, NULL);
sp = unit_getspell(u, s, u->faction->locale);
/* Vertraute koennen auch Zauber sprechen, die sie selbst nicht
* koennen. unit_getspell findet aber nur jene Sprueche, die
* die Einheit beherrscht. */
if (!sp && is_familiar(u)) {
mage = get_familiar_mage(u);
if (mage) {
familiar = u;
sp = unit_getspell(mage, s, mage->faction->locale);
}
else {
/* somehow, this familiar has no mage! */
log_error("cast_cmd: familiar %s is without a mage?\n", unitname(u));
/*
* u = Die Einheit, die den Befehl gegeben hat.
* mage = Die Einheit, deren Spruchliste und Aura benutzt wird.
*
* Vertraute koennen auch Zauber sprechen, die sie selbst nicht
* koennen. `unit_getspell` findet aber nur jene Sprueche, die
* die Einheit beherrscht. In diesem Fall ist `familiar` der Vertraute.
*/
if (sp) {
/* wir zaubern selbst */
mage = u;
}
else if (skill > 0) {
/* als Vertrauter suchen wir einen Spender-Magier mit dem Spruch */
mage = get_familiar_mage(u);
if (mage) {
int limit = effskill(mage, SK_MAGIC, NULL) / 2;
if (limit < skill) {
skill = limit;
}
sp = unit_getspell(mage, s, mage->faction->locale);
if (sp->sptyp & NOTFAMILIARCAST) {
/* Fehler: "Diesen Spruch kann der Vertraute nicht zaubern" */
cmistake(u, ord, 177, MSG_MAGIC);
return 0;
}
familiar = u;
}
}
/* OBS: hier kein else! */
if (!sp) {
/* Fehler 'Spell not found' */
cmistake(u, ord, 173, MSG_MAGIC);
return 0;
}
/* um testen auf spruchnamen zu unterbinden sollte vor allen
* fehlermeldungen die anzeigen das der magier diesen Spruch
* nur in diese Situation nicht anwenden kann, noch eine
* einfache Sicherheitspruefung kommen */
if (!knowsspell(r, u, sp)) {
/* vorsicht! u kann der familiar sein */
if (!familiar) {
cmistake(u, ord, 173, MSG_MAGIC);
return 0;
}
}
if (sp->sptyp & ISCOMBATSPELL) {
/* Fehler: "Dieser Zauber ist nur im Kampf sinnvoll" */
cmistake(u, ord, 174, MSG_MAGIC);
@ -2627,19 +2641,45 @@ static castorder *cast_cmd(unit * u, order * ord)
cmistake(u, ord, 176, MSG_MAGIC);
return 0;
}
if (range > 1024) { /* (2^10) weiter als 10 Regionen entfernt */
if (familiar) {
/* Magier zaubert durch Vertrauten: keine Fernzauber erlaubt */
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "familiar_farcast",
"mage", mage));
return 0;
}
if (range > 1024) {
/* (2^10) weiter als 10 Regionen entfernt */
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "spellfail::nocontact",
"target", target_r));
return 0;
}
}
if (familiar) {
/*
* Magier zaubert durch Vertrauten: Doppelte Kosten.
*
* Das hier über range zu machen ist ein Hack, deshalb passiert es erst
* nach allen anderen Range-Checks.
*/
range *= 2;
}
skill = effskill(mage, SK_MAGIC, NULL);
if (level < 0) {
level = default_spell_level(get_mage(mage), sp);
/**
* level = Die Stufe, auf der gezaubert werden soll.
* Kann nicht höher sein als das Talent des Zaubernden, oder im
* Falle, dass ein Vertrauter einen Spruch seines Magiers zaubert,
* nicht höher als dessen halbes Talent.
*/
if (level < 0) {
level = default_spell_level(mage, sp);
if (level <= 0) {
level = skill;
}
if (level > skill) {
/* die Einheit ist nicht erfahren genug fuer diesen Zauber */
cmistake(u, ord, 169, MSG_MAGIC);
return 0;
}
}
else if (!(sp->sptyp & SPELLLEVEL)) {
/* Stufenangabe bei nicht Stufenvariierbaren Spruechen abfangen */
@ -2649,45 +2689,12 @@ static castorder *cast_cmd(unit * u, order * ord)
"mage region command", u, u->region, ord));
}
}
if (level > skill) {
/* die Einheit ist nicht erfahren genug fuer diesen Zauber */
cmistake(u, ord, 169, MSG_MAGIC);
return 0;
/* STUFE kann nicht mehr als das erlaubte Maximum sein */
level = skill;
}
/* Vertrautenmagie */
/* Kennt der Vertraute den Spruch, so zaubert er ganz normal.
* Ansonsten zaubert der Magier durch seinen Vertrauten, dh
* zahlt Komponenten und Aura. Dabei ist die maximale Stufe
* die des Vertrauten!
* Der Spruch wirkt dann auf die Region des Vertrauten und
* gilt nicht als Farcasting. */
if (familiar) {
if ((sp->sptyp & NOTFAMILIARCAST)) {
/* Fehler: "Diesen Spruch kann der Vertraute nicht zaubern" */
cmistake(u, ord, 177, MSG_MAGIC);
return 0;
}
if (mage != familiar) { /* Magier zaubert durch Vertrauten */
int sk;
if (range > 1) { /* Fehler! Versucht zu Farcasten */
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "familiar_farcast",
"mage", mage));
return 0;
}
sk = effskill(mage, SK_MAGIC, NULL);
if (distance(mage->region, r) > sk) {
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "familiar_toofar",
"mage", mage));
return 0;
}
/* mage auf magier setzen, level anpassen, range fuer Erhoehung
* der Spruchkosten nutzen */
range *= 2;
sk /= 2;
if (level > sk) level = sk;
}
}
/* Weitere Argumente zusammenbasteln */
if (sp->parameter) {
char *params[MAX_PARAMETERS];

View File

@ -192,7 +192,6 @@ extern "C" {
enum magic_t mage_get_type(const struct sc_mage *mage);
const struct spell *mage_get_combatspell(const struct sc_mage *mage, int nr, int *level);
struct spellbook * mage_get_spellbook(const struct sc_mage * mage);
int mage_get_spell_level(const struct sc_mage *mage, const struct spell *sp);
int mage_get_spellpoints(const struct sc_mage *m);
void mage_set_spellpoints(struct sc_mage *m, int aura);
int mage_change_spellpoints(struct sc_mage *m, int delta);
@ -201,6 +200,7 @@ extern "C" {
void unit_set_magic(struct unit *u, enum magic_t mtype);
struct spellbook * unit_get_spellbook(const struct unit * u);
void unit_add_spell(struct unit * u, struct spell * sp, int level);
int unit_spell_level(const struct unit *u, const struct spell *sp);
bool is_mage(const struct unit *u);
/* gibt true, wenn u->mage gesetzt. */
@ -222,7 +222,9 @@ extern "C" {
bool u_hasspell(const struct unit *u, const struct spell *sp);
/* prueft, ob der Spruch in der Spruchliste der Einheit steht. */
void pick_random_spells(struct faction *f, int level, struct spellbook * book, int num_spells);
bool knowsspell(const struct region *r, const struct unit *u,
bool knowsspell(
const struct region *r,
const struct unit *u,
const struct spell * sp);
/* prueft, ob die Einheit diesen Spruch gerade beherrscht, dh
* mindestens die erforderliche Stufe hat. Hier koennen auch Abfragen