forked from github/server
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:
parent
bb12ffa2a2
commit
4d8a3d1947
13 changed files with 313 additions and 247 deletions
|
@ -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.
|
||||
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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.\""
|
||||
|
||||
|
|
|
@ -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.\""
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
131
scripts/tests/e2/migration.lua
Normal file
131
scripts/tests/e2/migration.lua
Normal 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
|
||||
]]--
|
|
@ -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
|
||||
|
|
38
src/battle.c
38
src/battle.c
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
302
src/magic.c
302
src/magic.c
|
@ -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);
|
||||
|
|
14
src/magic.h
14
src/magic.h
|
@ -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) */
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue