using a ring does not increase the spell's cost
adding tests for spell effectiveness revealed many inconsistencies that are now fixed.
This commit is contained in:
Enno Rehling 2021-04-05 19:19:49 +02:00
parent bb12ffa2a2
commit 4d8a3d1947
13 changed files with 313 additions and 247 deletions

View File

@ -4,12 +4,15 @@
- Dämonen können magisch reanimiert werden.
- "Schöne Träume" verliert seine Wirkung, wenn der Zauberer stirbt.
- Mit GIB 0 können hungernde Personen an die Bauern gegeben werden.
- Magieresistenz: Einheiten widerstehen nicht Zaubern der eigenen Partei [2733].
- Zauberkosten steigen durch Ring der Macht nicht an.
- Effektiv gezauberte Stufe von Zauber anhängig von Verfügbarkeit der Materialen.
# 3.27
- Schiffe sind kommentarlos nicht nicht gesegelt [2722]
- Meermenschen konnten nicht mehr anschwimmen [2723]
- Magieresistenz repariert [2724]
- Schiffe sind kommentarlos nicht gesegelt [2722].
- Meermenschen konnten nicht mehr anschwimmen [2723].
- Magieresistenz repariert [2724].
- Kleine Änderung an Samenwachstum.
- Umstellung auf neue Versionen von externen Libraries.

View File

@ -3807,13 +3807,6 @@
<arg name="command" type="order"/>
</type>
</message>
<message name="error185" section="errors">
<type>
<arg name="unit" type="unit"/>
<arg name="region" type="region"/>
<arg name="command" type="order"/>
</type>
</message>
<message name="error187" section="errors">
<type>
<arg name="unit" type="unit"/>

View File

@ -1937,9 +1937,6 @@ msgstr "\"Albträume plagen die Leute. ($int36($id))\""
msgid "error295"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Nur ein Magier kann einen Astralkristall benutzen.\""
msgid "error185"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Der Zauber scheint ungewöhnlich schwach zu sein. Irgendetwas hat die magischen Energien abgeleitet.\""
msgid "error181"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Dazu muß sich der Magier in der Burg oder an Bord des Schiffes befinden.\""

View File

@ -1937,9 +1937,6 @@ msgstr "\"Nightmares plague the population. ($int36($id))\""
msgid "error295"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Only mages may use an astralcrystal.\""
msgid "error185"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - The spell seems exceptionally weak. Something has interfred with the magical energies.\""
msgid "error181"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - To do this, the magician has to be in a castle or on board a ship.\""

View File

@ -1,3 +1,4 @@
require 'tests.e2.migration'
require 'tests.e2.trolls'
require 'tests.e2.trees'
require 'tests.e2.buildings'
@ -5,6 +6,7 @@ require 'tests.e2.movement'
require 'tests.e2.carts'
require 'tests.e2.astral'
require 'tests.e2.spells'
require 'tests.e2.migration'
require 'tests.e2.e2features'
require 'tests.e2.insects'
require 'tests.e2.production'
@ -30,6 +32,5 @@ require 'tests.magicbag'
require 'tests.process'
require 'tests.xmas'
require 'tests.production'
require 'tests.spells'
require 'tests.undead'
require 'tests.spells'

View File

@ -0,0 +1,131 @@
local tcname = 'tests.e2.migration'
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_mage(f, r)
local u = unit.create(f, r)
u.magic = 'tybied'
u:set_skill('magic', 10)
u:add_spell('migration')
return u
end
function test_migration_success()
local r = region.create(0, 0, "plain")
local f = faction.create('human')
local u = setup_mage(f, r)
local u2 = unit.create(faction.create('human'), r)
u2:add_order('KONTAKTIERE ' .. itoa36(u.id))
u:add_order('ZAUBERE STUFE 1 "Ritual der Aufnahme" ' .. itoa36(u2.id))
u.aura = 9
u.aura_max = 9
process_orders()
assert_equal(f, u2.faction)
assert_equal(6, u.aura)
assert_equal(8, u.aura_max)
end
function test_migration_no_contact()
local r = region.create(0, 0, "plain")
local f = faction.create('human')
local u = setup_mage(f, r)
local u2 = unit.create(faction.create('human'), r)
u:add_order('ZAUBERE STUFE 1 "Ritual der Aufnahme" ' .. itoa36(u2.id))
u.aura = 9
u.aura_max = 9
process_orders()
assert_not_equal(f, u2.faction)
assert_equal(9, u.aura)
assert_equal(9, u.aura_max)
end
function test_migration_too_many()
local r = region.create(0, 0, "plain")
local f = faction.create('human')
local u = setup_mage(f, r)
local u2 = unit.create(faction.create('human'), r)
u2:add_order('KONTAKTIERE ' .. itoa36(u.id))
u2.number = 2
u:add_order('ZAUBERE STUFE 1 "Ritual der Aufnahme" ' .. itoa36(u2.id))
u.aura = 9
u.aura_max = 9
process_orders()
assert_not_equal(f, u2.faction)
assert_equal(9, u.aura)
assert_equal(9, u.aura_max)
end
function test_migration_with_ring()
local r = region.create(0, 0, "plain")
local f = faction.create('human')
local u = setup_mage(f, r)
local u2 = unit.create(faction.create('human'), r)
u2:add_order('KONTAKTIERE ' .. itoa36(u.id))
u2.number = 2
u:add_item('rop', 1)
u:add_order('ZAUBERE STUFE 1 "Ritual der Aufnahme" ' .. itoa36(u2.id))
u.aura = 9
u.aura_max = 9
process_orders()
assert_equal(f, u2.faction)
assert_equal(6, u.aura)
assert_equal(8, u.aura_max)
end
function test_migration_insufficient_aura()
-- if unit cannot pay full costs, it casts at a lower level.
local r = region.create(0, 0, "plain")
local f = faction.create('human')
local u = setup_mage(f, r)
local u2 = unit.create(faction.create('human'), r)
u2:add_order('KONTAKTIERE ' .. itoa36(u.id))
u2.number = 2
u:add_order('ZAUBERE STUFE 2 "Ritual der Aufnahme" ' .. itoa36(u2.id))
u.aura = 3
u.aura_max = 9
process_orders()
-- spell fails, costs nothing:
assert_not_equal(f, u2.faction)
assert_equal(3, u.aura)
assert_equal(9, u.aura_max)
end
function test_migration_reduced_cost()
-- if unit cannot pay full costs, it casts at a lower level.
local r = region.create(0, 0, "plain")
local f = faction.create('human')
local u = setup_mage(f, r)
local u2 = unit.create(faction.create('human'), r)
u2:add_order('KONTAKTIERE ' .. itoa36(u.id))
u:add_order('ZAUBERE STUFE 2 "Ritual der Aufnahme" ' .. itoa36(u2.id))
u.aura = 3
u.aura_max = 9
process_orders()
-- spell is cast at level 1:
assert_equal(f, u2.faction)
assert_equal(0, u.aura)
assert_equal(7, u.aura_max)
end
--[[
additional tests:
- not enough aura, casting at lower level
- no aura, ring does not grant level 1
- magic tower, like ring, cumulative
]]--

View File

@ -163,11 +163,10 @@ local function create_cp_front(f, r)
end
function test_confusion_and_panic()
f = faction.create('demon', "confusion@eressea.de", "de")
f2 = faction.create('demon')
for y = 1, 10 do
f = faction.create('demon')
f2 = faction.create('demon')
local u1, u2, u3, u4, r
r = region.create(0, 2*y, 'plain')
r = region.create(0, 0, 'plain')
u1 = create_cp_front(f, r)
u2 = create_cp_mage(f, r)
u3 = create_cp_mage(f, r)
@ -181,18 +180,11 @@ function test_confusion_and_panic()
create_cp_mage(f2, r)
for ux in r.units do
for uy in r.units do
if ux.faction ~= uy.faction then
ux:add_order("ATTACKIERE " .. itoa36(uy.id))
for uy in r.units do
if ux.faction ~= uy.faction then
ux:add_order("ATTACKIERE " .. itoa36(uy.id))
end
end
end
end
end
for i = 1,10 do
process_orders()
end
-- should not produce "select_enemies has a bug"
-- init_reports()
-- write_reports()
end

View File

@ -1711,26 +1711,20 @@ void do_combatmagic(battle * b, combatmagic_t was)
/* Fehler! */
return;
}
if (sp == NULL)
if (sp == NULL || !u_hasspell(mage, sp))
continue;
ord = create_order(K_CAST, lang, "'%s'", spell_name(mkname_spell(sp), lang));
if (!cancast(mage, sp, 1, 1, ord)) {
free_order(ord);
continue;
}
level = eff_spelllevel(mage, caster, sp, level, 1);
level = max_spell_level(mage, caster, sp, level, 1, NULL);
if (sl > 0 && sl < level) {
level = sl;
}
if (level < 0) {
if (level < 1) {
report_failed_spell(b, mage, sp);
free_order(ord);
continue;
}
power = spellpower(r, mage, sp, level, ord);
ord = create_order(K_CAST, lang, "'%s'", spell_name(mkname_spell(sp), lang));
power = spellpower(r, mage, sp, level);
free_order(ord);
if (power <= 0) { /* Effekt von Antimagie */
report_failed_spell(b, mage, sp);
@ -1787,25 +1781,23 @@ static void do_combatspell(troop at)
int level, qi;
double power;
int fumblechance = 0;
order *ord;
int sl;
const struct locale *lang = mage->faction->locale;
sp = get_combatspell(mage, 1);
if (sp == NULL) {
if (sp == NULL || !u_hasspell(mage, sp)) {
fi->magic = 0; /* Hat keinen Kampfzauber, kaempft nichtmagisch weiter */
return;
}
ord = create_order(K_CAST, lang, "'%s'", spell_name(mkname_spell(sp), lang));
if (!cancast(mage, sp, 1, 1, ord)) {
level = max_spell_level(mage, mage, sp, fi->magic, 1, NULL);
if (level < 1) {
fi->magic = 0; /* Kann nicht mehr Zaubern, kaempft nichtmagisch weiter */
return;
}
level = eff_spelllevel(mage, mage, sp, fi->magic, 1);
sl = get_combatspelllevel(mage, 1);
if (sl > 0 && sl < level) {
level = sl;
else {
int sl = get_combatspelllevel(mage, 1);
if (sl < level) {
level = sl;
}
}
if (fumble(r, mage, sp, level)) {
@ -1828,11 +1820,9 @@ static void do_combatspell(troop at)
if (rng_int() % 100 < fumblechance) {
report_failed_spell(b, mage, sp);
pay_spell(mage, NULL, sp, level, 1);
free_order(ord);
return;
}
power = spellpower(r, mage, sp, level, ord);
free_order(ord);
power = spellpower(r, mage, sp, level);
if (power <= 0) { /* Effekt von Antimagie */
report_failed_spell(b, mage, sp);
pay_spell(mage, NULL, sp, level, 1);

View File

@ -194,7 +194,7 @@ static int tolua_unit_get_id(lua_State * L)
static int tolua_unit_set_id(lua_State * L)
{
unit *u = (unit *)tolua_tousertype(L, 1, 0);
unit_setid(u, (int)tolua_tonumber(L, 2, 0));
unit_setid(u, (int)lua_tointeger(L, 2));
return 0;
}
@ -205,6 +205,17 @@ static int tolua_unit_get_auramax(lua_State * L)
return 1;
}
static int tolua_unit_set_auramax(lua_State * L)
{
unit *u = (unit *)tolua_tousertype(L, 1, 0);
int aura = (int)lua_tointeger(L, 2);
int now = max_spellpoints(u, u->region);
aura = change_maxspellpoints(u, aura - now);
lua_pushinteger(L, aura);
return 1;
}
static int tolua_unit_get_hpmax(lua_State * L)
{
unit *u = (unit *)tolua_tousertype(L, 1, 0);
@ -1055,7 +1066,7 @@ void tolua_unit_open(lua_State * L)
tolua_variable(L, TOLUA_CAST "race", tolua_unit_get_race,
tolua_unit_set_race);
tolua_variable(L, TOLUA_CAST "hp_max", tolua_unit_get_hpmax, 0);
tolua_variable(L, TOLUA_CAST "aura_max", tolua_unit_get_auramax, 0);
tolua_variable(L, TOLUA_CAST "aura_max", tolua_unit_get_auramax, tolua_unit_set_auramax);
tolua_function(L, TOLUA_CAST "equip", tolua_equipunit);
tolua_function(L, TOLUA_CAST "show", tolua_bufunit);

View File

@ -1374,9 +1374,10 @@ static void end_spells(parseinfo *pi, const XML_Char *el) {
else if (xml_strequal(el, "spell")) {
spell *sp = (spell *)pi->object;
if (ncomponents > 0) {
sp->components = (spell_component *)calloc(ncomponents + 1, sizeof(spell_component));
sp->components = malloc((1 + (size_t)ncomponents) * sizeof(spell_component));
if (!sp->components) abort();
memcpy(sp->components, components, sizeof(spell_component) * ncomponents);
sp->components[ncomponents].type = NULL;
ncomponents = 0;
}
pi->object = NULL;

View File

@ -267,12 +267,16 @@ 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;
spellbook *book = get_spellbook(magic_school[mage->magietyp]);
spellbook_entry *sbe = spellbook_get(book, sp);
return sbe ? sbe->level : 0;
return mage_get_spell_level(mage, sp);
}
/* ------------------------------------------------------------- */
@ -491,14 +495,14 @@ sc_mage *create_mage(unit * u, magic_t mtyp)
/* ------------------------------------------------------------- */
/* Funktionen fuer die Bearbeitung der List-of-known-spells */
int u_hasspell(const unit *u, const struct spell *sp)
bool u_hasspell(const unit *u, const struct spell *sp)
{
spellbook * book = unit_get_spellbook(u);
spellbook_entry * sbe = book ? spellbook_get(book, sp) : 0;
if (sbe) {
return sbe->level <= effskill(u, SK_MAGIC, NULL);
}
return 0;
return false;
}
/* ------------------------------------------------------------- */
@ -721,100 +725,67 @@ int auracost(const unit *caster, const spell *sp) {
return 0;
}
/* ------------------------------------------------------------- */
/* SPC_LINEAR ist am hoechstwertigen, dann muessen Komponenten fuer die
* Stufe des Magiers vorhanden sein.
* SPC_LINEAR hat die gewuenschte Stufe als multiplikator,
* nur SPC_FIX muss nur einmal vorhanden sein, ist also am
* niedrigstwertigen und sollte von den beiden anderen Typen
* ueberschrieben werden */
static int spl_costtyp(const spell * sp)
{
int k;
int costtyp = SPC_FIX;
for (k = 0; sp->components && sp->components[k].type; k++) {
if (costtyp == SPC_LINEAR)
return SPC_LINEAR;
if (sp->components[k].cost == SPC_LINEAR) {
return SPC_LINEAR;
}
/* wenn keine Fixkosten, Typ uebernehmen */
if (sp->components[k].cost != SPC_FIX) {
costtyp = sp->components[k].cost;
}
static void add_missing_component(resource **reslist_p, const struct spell_component *spc, int n) {
resource *res = malloc(sizeof(resource));
if (res) {
res->number = n;
res->type = spc->type;
res->next = *reslist_p;
*reslist_p = res;
}
return costtyp;
}
/**
* Durch Komponenten und cast_level begrenzter maximal moeglicher Level.
*
* Da die Funktion nicht alle Komponenten durchprobiert sondern beim
* ersten Fehler abbricht, muss die Fehlermeldung spaeter mit cancast()
* generiert werden.
*/
int eff_spelllevel(unit * u, unit *caster, const spell * sp, int cast_level, int range)
int max_spell_level(unit * u, unit *caster, const spell * sp, int cast_level, int range, resource **reslist_p)
{
const resource_type *r_aura = get_resourcetype(R_AURA);
int k, maxlevel;
int costtyp = SPC_FIX;
const struct spell_component *spc;
int maxlevel = cast_level;
for (k = 0; sp->components && sp->components[k].type; k++) {
if (cast_level == 0)
return 0;
if (!sp->components) {
return cast_level;
}
if (sp->components[k].amount > 0) {
int level_cost = sp->components[k].amount * range;
if (sp->components[k].type == r_aura) {
/* Die Kosten fuer Aura sind auch von der Zahl der bereits
* gezauberten Sprueche abhaengig */
level_cost *= aura_multiplier(caster);
}
maxlevel =
get_pooled(u, sp->components[k].type, GET_DEFAULT,
level_cost * cast_level) / level_cost;
for (spc = sp->components; spc->type; ++spc) {
if (spc->amount > 0) {
int need = 0, have, level_cost = spellcost(u, spc) * range;
if (level_cost <= 0) continue;
have = get_pooled(u, spc->type, GET_DEFAULT,
level_cost * cast_level);
/* sind die Kosten fix, so muss die Komponente nur einmal vorhanden
* sein und der cast_level aendert sich nicht */
if (sp->components[k].cost == SPC_FIX) {
if (maxlevel < 1)
cast_level = 0;
/* ansonsten wird das Minimum aus maximal moeglicher Stufe und der
* gewuenschten gebildet */
}
else if (sp->components[k].cost == SPC_LEVEL) {
costtyp = SPC_LEVEL;
if (maxlevel < cast_level) {
cast_level = maxlevel;
* sein und der cast_level aendert sich nicht,
* ansonsten wird das Minimum aus maximal moeglicher Stufe und der
* gewuenschten gebildet */
if (spc->cost == SPC_FIX) {
if (have < level_cost) {
maxlevel = 0;
need = level_cost - have;
}
/* bei Typ Linear muessen die Kosten in Hoehe der Stufe vorhanden
* sein, ansonsten schlaegt der Spruch fehl */
}
else if (sp->components[k].cost == SPC_LINEAR) {
costtyp = SPC_LINEAR;
if (maxlevel < cast_level)
cast_level = 0;
else {
need = level_cost * cast_level;
if (have < need) {
/* bei Typ Linear muessen die Kosten in Hoehe der Stufe vorhanden
* sein, ansonsten schlaegt der Spruch fehl */
if (spc->cost == SPC_LINEAR) {
maxlevel = 0;
}
else {
maxlevel = have / level_cost;
}
need -= have;
}
}
if (reslist_p && need > 0) {
add_missing_component(reslist_p, spc, need);
}
}
}
/* Ein Spruch mit Fixkosten wird immer mit der Stufe des Spruchs und
* nicht auf der Stufe des Magiers gezaubert */
if (costtyp == SPC_FIX) {
spellbook * sb = unit_get_spellbook(u);
if (sb) {
spellbook_entry * sbe = spellbook_get(sb, sp);
if (sbe && cast_level > sbe->level) {
return sbe->level;
}
}
else {
log_error("spell %s is not in the spellbook for %s\n", sp->sname, unitname(u));
}
}
return cast_level;
return maxlevel;
}
/* ------------------------------------------------------------- */
@ -852,7 +823,7 @@ bool knowsspell(const region * r, const unit * u, const spell * sp)
UNUSED_ARG(r);
assert(sp);
/* steht der Spruch in der Spruchliste? */
return u_hasspell(u, sp) != 0;
return u_hasspell(u, sp);
}
/* Um einen Spruch zu beherrschen, muss der Magier die Stufe des
@ -862,13 +833,28 @@ bool knowsspell(const region * r, const unit * u, const spell * sp)
* und sonstige Gegenstaende sein.
*/
static void
report_missing_components(unit *u, order *ord, resource *reslist) {
assert(reslist);
assert(u);
assert(u->faction);
assert(ord);
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "missing_components_list",
"list", reslist));
}
static void free_components(resource *reslist) {
while (reslist) {
resource *res = reslist->next;
free(reslist);
reslist = res;
}
}
bool
cancast(unit * u, const spell * sp, int level, int range, struct order * ord)
{
int k;
resource *reslist = NULL;
if (!knowsspell(u->region, u, sp)) {
if (u_hasspell(u, sp)) {
/* Diesen Zauber kennt die Einheit nicht */
cmistake(u, ord, 173, MSG_MAGIC);
return false;
@ -880,49 +866,6 @@ cancast(unit * u, const spell * sp, int level, int range, struct order * ord)
return false;
}
for (k = 0; sp->components && sp->components[k].type; ++k) {
const struct spell_component *spc = sp->components + k;
if (spc->amount > 0) {
const resource_type *rtype = spc->type;
int itemhave, itemanz;
/* Die Kosten fuer Aura sind auch von der Zahl der bereits
* gezauberten Sprueche abhaengig */
itemanz = spellcost(u, spc) * range;
/* sind die Kosten stufenabhaengig, so muss itemanz noch mit dem
* level multipliziert werden */
switch (spc->cost) {
case SPC_LEVEL:
case SPC_LINEAR:
itemanz *= level;
break;
case SPC_FIX:
default:
break;
}
itemhave = get_pooled(u, rtype, GET_DEFAULT, itemanz);
if (itemhave < itemanz) {
resource *res = malloc(sizeof(resource));
assert(res);
res->number = itemanz - itemhave;
res->type = rtype;
res->next = reslist;
reslist = res;
}
}
}
if (reslist != NULL) {
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "missing_components_list",
"list", reslist));
while (reslist) {
resource *res = reslist->next;
free(reslist);
reslist = res;
}
return false;
}
return true;
}
@ -938,7 +881,7 @@ cancast(unit * u, const spell * sp, int level, int range, struct order * ord)
*/
double
spellpower(region * r, unit * u, const spell * sp, int cast_level, struct order *ord)
spellpower(region * r, unit * u, const spell * sp, int cast_level)
{
double force = cast_level;
static int elf_power, config;
@ -981,7 +924,6 @@ spellpower(region * r, unit * u, const spell * sp, int cast_level, struct order
unit *mage = c->magician;
force -= curse_geteffect(c);
curse_changevigour(&r->attribs, c, -cast_level);
cmistake(u, ord, 185, MSG_MAGIC);
if (mage != NULL && mage->faction != NULL) {
if (force > 0) {
ADDMSG(&mage->faction->msgs, msg_message("reduce_spell",
@ -1000,7 +942,6 @@ spellpower(region * r, unit * u, const spell * sp, int cast_level, struct order
unit *mage = c->magician;
force -= curse_geteffect(c);
curse_changevigour(&u->attribs, c, -1);
cmistake(u, ord, 185, MSG_MAGIC);
if (mage != NULL && mage->faction != NULL) {
if (force > 0) {
ADDMSG(&mage->faction->msgs, msg_message("reduce_spell",
@ -2532,16 +2473,28 @@ static bool is_moving_ship(ship * sh)
return false;
}
static int default_spell_level(const sc_mage *mage, 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 mage_get_spell_level(mage, sp);
}
#define MAX_PARAMETERS 48
static castorder *cast_cmd(unit * u, order * ord)
{
char token[128];
region *r = u->region;
region *target_r = r;
int level, range;
int level = -1, range, skill;
unit *familiar = NULL;
const char *s;
spell *sp = 0;
spell *sp = NULL;
plane *pl;
spellparameter *args = NULL;
unit * mage = u;
@ -2556,20 +2509,13 @@ static castorder *cast_cmd(unit * u, order * ord)
cmistake(u, ord, 269, MSG_MAGIC);
return 0;
}
level = effskill(u, SK_MAGIC, NULL);
init_order(ord, NULL);
s = gettoken(token, sizeof(token));
param = findparam(s, u->faction->locale);
/* fuer Syntax ' STUFE x REGION y z ' */
if (param == P_LEVEL) {
int p = getint();
if (level > p) level = p;
if (level < 1) {
/* Fehler "Das macht wenig Sinn" */
syntax_error(u, ord);
return 0;
}
level = getint();
s = gettoken(token, sizeof(token));
param = findparam(s, u->faction->locale);
}
@ -2590,16 +2536,9 @@ static castorder *cast_cmd(unit * u, order * ord)
s = gettoken(token, sizeof(token));
param = findparam(s, u->faction->locale);
}
/* fuer Syntax ' REGION x y STUFE z '
* hier nach REGION nochmal auf STUFE pruefen */
/* fuer Syntax `REGION x y STUFE z` hier nach REGION nochmal pruefen */
if (param == P_LEVEL) {
int p = getint();
if (level > p) level = p;
if (level < 1) {
/* Fehler "Das macht wenig Sinn" */
syntax_error(u, ord);
return 0;
}
level = getint();
s = gettoken(token, sizeof(token));
}
if (!s || !s[0]) {
@ -2609,7 +2548,6 @@ static castorder *cast_cmd(unit * u, order * ord)
}
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. */
@ -2631,6 +2569,7 @@ static castorder *cast_cmd(unit * u, order * ord)
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
@ -2647,6 +2586,7 @@ static castorder *cast_cmd(unit * u, order * ord)
cmistake(u, ord, 174, MSG_MAGIC);
return 0;
}
/* Auf dem Ozean Zaubern als quasi-langer Befehl koennen
* normalerweise nur Meermenschen, ausgenommen explizit als
* OCEANCASTABLE deklarierte Sprueche */
@ -2687,15 +2627,28 @@ static castorder *cast_cmd(unit * u, order * ord)
return 0;
}
}
/* Stufenangabe bei nicht Stufenvariierbaren Spruechen abfangen */
if (!(sp->sptyp & SPELLLEVEL)) {
int ilevel = effskill(u, SK_MAGIC, NULL);
if (ilevel != level) {
level = ilevel;
skill = effskill(mage, SK_MAGIC, NULL);
if (level < 0) {
level = default_spell_level(get_mage(mage), sp);
if (level < 0) {
level = skill;
}
}
else if (!(sp->sptyp & SPELLLEVEL)) {
/* Stufenangabe bei nicht Stufenvariierbaren Spruechen abfangen */
if (skill != level) {
level = skill;
ADDMSG(&u->faction->msgs, msg_message("spellfail::nolevel",
"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;
}
/* Vertrautenmagie */
/* Kennt der Vertraute den Spruch, so zaubert er ganz normal.
* Ansonsten zaubert der Magier durch seinen Vertrauten, dh
@ -2832,33 +2785,28 @@ void magic(void)
unit *caster = co_get_caster(co);
const spell *sp = co->sp;
region *target_r = co_get_region(co);
resource *reslist = NULL;
/* reichen die Komponenten nicht, wird der Level reduziert. */
co->level = eff_spelllevel(mage, caster, sp, cast_level, co->distance);
/* reichen die Komponenten nicht, kann der Level reduziert werden. */
co->level = max_spell_level(mage, caster, sp, cast_level, co->distance, &reslist);
if (co->level < 1) {
/* Fehlermeldung mit Komponenten generieren */
cancast(mage, sp, cast_level, co->distance, ord);
/* Es fehlt eine Komponente vollständig: */
assert(reslist);
report_missing_components(mage, ord, reslist);
free_components(reslist);
continue;
}
if (cast_level > co->level) {
/* Sprueche mit Fixkosten werden immer auf Stufe des Spruchs
* gezaubert, co->level ist aber defaultmaessig Stufe des Magiers */
if (spl_costtyp(sp) != SPC_FIX) {
ADDMSG(&caster->faction->msgs, msg_message("missing_components",
"unit spell level", caster, sp, cast_level));
}
/* Es gibt von einer Komponente zu wenig, deshalb
* wird mit reduzierter Stufe gezaubert: */
ADDMSG(&caster->faction->msgs, msg_message("missing_components",
"unit spell level", caster, sp, cast_level));
free_components(reslist);
}
/* Pruefen, ob die realen Kosten fuer die gewuenschten Stufe bezahlt
* werden koennen */
if (!cancast(mage, sp, co->level, co->distance, ord)) {
/* die Fehlermeldung wird in cancast generiert */
continue;
}
co->force = MagicPower(spellpower(target_r, mage, sp, co->level, ord));
co->force = MagicPower(spellpower(target_r, mage, sp, co->level));
/* die Staerke kann durch Antimagie auf 0 sinken */
if (co->force <= 0) {
co->force = 0;
@ -2905,7 +2853,7 @@ void magic(void)
}
/* erst bezahlen, dann Kostenzaehler erhoehen */
if (co->level > 0) {
pay_spell(mage, caster, sp, co->level, co->distance);
pay_spell(mage, caster, sp, cast_level, co->distance);
}
if (fumbled) {
do_fumble(co);

View File

@ -112,7 +112,7 @@ extern "C" {
typedef struct spell_component {
const struct resource_type *type;
int amount;
int cost;
enum spellcost_t cost;
} spell_component;
/* ------------------------------------------------------------- */
@ -146,7 +146,7 @@ extern "C" {
#define ANYTARGET (UNITSPELL|REGIONSPELL|BUILDINGSPELL|SHIPSPELL) /* wirkt auf alle objekttypen (unit, ship, building, region) */
/* Flag Spruchkostenberechnung: */
enum {
enum spellcost_t {
SPC_FIX, /* Fixkosten */
SPC_LEVEL, /* Komponenten pro Level */
SPC_LINEAR /* Komponenten pro Level und muessen vorhanden sein */
@ -191,6 +191,7 @@ 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);
@ -217,7 +218,7 @@ extern "C" {
void unset_combatspell(struct unit *u, struct spell * sp);
/* loescht Kampfzauber */
/* fuegt den Spruch mit der Id spellid der Spruchliste der Einheit hinzu. */
int u_hasspell(const struct unit *u, const struct spell *sp);
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,
@ -242,7 +243,7 @@ extern "C" {
/* Zaubern */
double spellpower(struct region *r, struct unit *u, const struct spell * sp,
int cast_level, struct order *ord);
int cast_level);
/* ermittelt die Staerke eines Spruchs */
bool fumble(struct region *r, struct unit *u, const struct spell * sp,
int cast_level);
@ -282,8 +283,9 @@ extern "C" {
/* zieht die Komponenten des Zaubers aus dem Inventory der Einheit
* ab. Die effektive Stufe des gezauberten Spruchs ist wichtig fuer
* die korrekte Bestimmung der Magiepunktkosten */
int eff_spelllevel(struct unit *mage, struct unit *caster,
const struct spell * sp, int cast_level, int distance);
int max_spell_level(struct unit *mage, struct unit *caster,
const struct spell * sp, int cast_level, int distance,
struct resource **reslist_p);
/* ermittelt die effektive Stufe des Zaubers. Dabei ist cast_level
* die gewuenschte maximale Stufe (im Normalfall Stufe des Magiers,
* bei Farcasting Stufe*2^Entfernung) */

View File

@ -127,7 +127,7 @@ void test_pay_spell(CuTest * tc)
change_resource(u, get_resourcetype(R_AURA), 3);
change_resource(u, get_resourcetype(R_HORSE), 3);
level = eff_spelllevel(u, u, sp, 3, 1);
level = max_spell_level(u, u, sp, 3, 1, NULL);
CuAssertIntEquals(tc, 3, level);
pay_spell(u, NULL, sp, level, 1);
CuAssertIntEquals(tc, 0, get_resource(u, get_resourcetype(R_SILVER)));
@ -161,16 +161,16 @@ void test_pay_spell_failure(CuTest * tc)
CuAssertIntEquals(tc, 2, change_resource(u, get_resourcetype(R_AURA), 2));
CuAssertIntEquals(tc, 3, change_resource(u, get_resourcetype(R_HORSE), 3));
level = eff_spelllevel(u, u, sp, 3, 1);
level = max_spell_level(u, u, sp, 3, 1, NULL);
CuAssertIntEquals(tc, 2, level);
pay_spell(u, NULL, sp, level, 1);
CuAssertIntEquals(tc, 1, change_resource(u, get_resourcetype(R_SILVER), 1));
CuAssertIntEquals(tc, 3, change_resource(u, get_resourcetype(R_AURA), 3));
CuAssertIntEquals(tc, 2, change_resource(u, get_resourcetype(R_HORSE), 1));
CuAssertIntEquals(tc, 0, eff_spelllevel(u, u, sp, 3, 1));
CuAssertIntEquals(tc, 0, max_spell_level(u, u, sp, 3, 1, NULL));
CuAssertIntEquals(tc, 0, change_resource(u, get_resourcetype(R_SILVER), -1));
CuAssertIntEquals(tc, 0, eff_spelllevel(u, u, sp, 2, 1));
CuAssertIntEquals(tc, 0, max_spell_level(u, u, sp, 2, 1, NULL));
test_teardown();
}