Merge branch 'develop' of github.com:ennorehling/eressea into develop

This commit is contained in:
Enno Rehling 2018-11-21 14:26:08 +01:00
commit 8e9f8be007
18 changed files with 708 additions and 559 deletions

View File

@ -96,7 +96,6 @@ function test_follow_ship()
local f = faction.create("human", "test@example.com", "de") local f = faction.create("human", "test@example.com", "de")
local u1 = unit.create(f, r1, 1) local u1 = unit.create(f, r1, 1)
local u2 = unit.create(f, r1, 1) local u2 = unit.create(f, r1, 1)
u2.name = 'Xolgrim'
u1:add_item("money", 100) u1:add_item("money", 100)
u2:add_item("money", 100) u2:add_item("money", 100)
u1.ship = ship.create(r1, "boat") u1.ship = ship.create(r1, "boat")

View File

@ -10,6 +10,7 @@ function setup()
eressea.settings.set("rules.food.flags", "4") eressea.settings.set("rules.food.flags", "4")
eressea.settings.set("rules.peasants.growth.factor", "0") eressea.settings.set("rules.peasants.growth.factor", "0")
eressea.settings.set("magic.fumble.enable", "0") eressea.settings.set("magic.fumble.enable", "0")
eressea.settings.set("magic.regeneration.enable", "0")
end end
function test_shapeshift() function test_shapeshift()
@ -104,6 +105,52 @@ function test_earn_silver()
assert_equal(0, r:get_resource("money")) assert_equal(0, r:get_resource("money"))
end end
function test_familiar_cast()
local r = region.create(0, 0, "plain")
r:set_resource("money", 350)
r:set_resource("peasant", 0)
local f = faction.create("human")
local u = unit.create(f, r)
u.magic = "gwyrrd"
u:set_skill("magic", 10)
u.aura = 200
u:add_spell("earn_silver#gwyrrd")
u:add_order('ARBEITE')
local uf = unit.create(f, r)
uf.race = "lynx"
uf:set_skill("magic", 5)
uf:add_order('ZAUBER STUFE 1 Viehheilung')
u.familiar = uf
process_orders()
assert_equal(198, u.aura) -- Fremdzauber, Kosten verdoppelt
assert_equal(10, u:get_item('money')) -- von ARBEITE
assert_equal(50, uf:get_item('money')) -- von Zauber
assert_equal(300, uf.region:get_resource("money"))
end
function test_familiar_mage_actions()
local r = region.create(0, 0, "plain")
r:set_resource("money", 350)
r:set_resource("peasant", 0)
local f = faction.create("human")
local u = unit.create(f, r)
u.magic = "gwyrrd"
u:set_skill("magic", 10)
u.aura = 200
u:add_spell("earn_silver#gwyrrd")
u:add_order('ZAUBER STUFE 1 Viehheilung')
local uf = unit.create(f, r)
uf.race = "lynx"
uf:set_skill("magic", 5)
uf:add_order('ZAUBER STUFE 1 Viehheilung')
u.familiar = uf
process_orders()
assert_equal(50, u:get_item('money'))
assert_equal(50, uf:get_item('money'))
assert_equal(250, uf.region:get_resource("money"))
assert_equal(197, u.aura)
end
function test_familiar() function test_familiar()
local r = region.create(0, 0, "mountain") local r = region.create(0, 0, "mountain")
local f = faction.create("human") local f = faction.create("human")
@ -148,3 +195,55 @@ function test_bug_2480()
process_orders() process_orders()
assert_equal(0, u1.number); assert_equal(0, u1.number);
end end
function test_bug_2517()
local r = region.create(0, 0, "plain")
local f = faction.create("elf")
local um = unit.create(f, r, 1)
local uf = nil
eressea.settings.set("magic.familiar.race", "lynx")
f.magic = 'gwyrrd'
um.name = 'Xolgrim'
um.magic = 'gwyrrd'
um.race = 'elf'
um:set_skill('magic', 10)
um:add_spell('summon_familiar')
um:add_spell('earn_silver#gwyrrd')
um:add_order('ZAUBERE Vertrauten~rufen')
um.aura = 200
process_orders()
uf = um.familiar
assert_not_nil(uf)
assert_equal('lynx', uf.race)
assert_nil(uf.magic)
uf:add_order('LERNE Magie')
um:clear_orders()
um:add_order('ARBEITEN')
process_orders()
assert_nil(uf.magic)
uf:add_order('ZAUBERE STUFE 1 Viehheilung')
process_orders()
assert_equal(50, uf:get_item('money'))
end
function test_familiar_school()
local r = region.create(0, 0, "plain")
r:set_resource("money", 350)
r:set_resource("peasant", 0)
local f = faction.create("human")
local u = unit.create(f, r)
u.magic = "draig"
u:set_skill("magic", 10)
u.aura = 200
u:add_spell("fireball")
local uf = unit.create(f, r)
uf.race = "lynx"
u.familiar = uf
assert_nil(uf.magic)
uf:set_skill("magic", 5)
assert_nil(uf.magic)
uf.aura = 10
assert_equal(0, uf.aura)
assert_nil(uf.magic)
end

View File

@ -67,6 +67,7 @@ void new_potiontype(item_type * itype, int level)
potion_type *ptype; potion_type *ptype;
ptype = (potion_type *)calloc(1, sizeof(potion_type)); ptype = (potion_type *)calloc(1, sizeof(potion_type));
assert(ptype);
itype->flags |= ITF_POTION; itype->flags |= ITF_POTION;
ptype->itype = itype; ptype->itype = itype;
ptype->level = level; ptype->level = level;

View File

@ -134,6 +134,7 @@ static int rule_cavalry_skill;
static int rule_population_damage; static int rule_population_damage;
static int rule_hero_speed; static int rule_hero_speed;
static bool rule_anon_battle; static bool rule_anon_battle;
static bool rule_igjarjuk_curse;
static int rule_goblin_bonus; static int rule_goblin_bonus;
static int rule_tactics_formula; static int rule_tactics_formula;
static int rule_nat_armor; static int rule_nat_armor;
@ -156,6 +157,7 @@ static void init_rules(void)
rule_hero_speed = config_get_int("rules.combat.herospeed", 10); rule_hero_speed = config_get_int("rules.combat.herospeed", 10);
rule_population_damage = config_get_int("rules.combat.populationdamage", 20); rule_population_damage = config_get_int("rules.combat.populationdamage", 20);
rule_anon_battle = config_get_int("rules.stealth.anon_battle", 1) != 0; rule_anon_battle = config_get_int("rules.stealth.anon_battle", 1) != 0;
rule_igjarjuk_curse = config_get_int("rules.combat.igjarjuk_curse", 0) != 0;
rule_cavalry_mode = config_get_int("rules.cavalry.mode", 1); rule_cavalry_mode = config_get_int("rules.cavalry.mode", 1);
rule_cavalry_skill = config_get_int("rules.cavalry.skill", 2); rule_cavalry_skill = config_get_int("rules.cavalry.skill", 2);
rule_vampire = config_get_int("rules.combat.demon_vampire", 0); rule_vampire = config_get_int("rules.combat.demon_vampire", 0);
@ -486,7 +488,7 @@ contest_classic(int skilldiff, const armor_type * ar, const armor_type * sh)
mod *= (1 + ar->penalty); mod *= (1 + ar->penalty);
if (sh != NULL) if (sh != NULL)
mod *= (1 + sh->penalty); mod *= (1 + sh->penalty);
vw = (int)(100 - ((100 - vw) * mod)); vw = (int)(100.0 - ((100.0 - (double)vw) * mod));
do { do {
p = (int)(rng_int() % 100); p = (int)(rng_int() % 100);
@ -1224,7 +1226,7 @@ static void calculate_attack_type(troop dt, troop at, int type, bool missile,
static int crit_damage(int attskill, int defskill, const char *damage_formula) { static int crit_damage(int attskill, int defskill, const char *damage_formula) {
int damage = 0; int damage = 0;
if (rule_damage & DAMAGE_CRITICAL) { if (rule_damage & DAMAGE_CRITICAL) {
double kritchance = (attskill * 3 - defskill) / 200.0; double kritchance = ((double)attskill * 3.0 - (double)defskill) / 200.0;
int maxk = 4; int maxk = 4;
kritchance = fmax(kritchance, 0.005); kritchance = fmax(kritchance, 0.005);
@ -1748,13 +1750,14 @@ void do_combatmagic(battle * b, combatmagic_t was)
memset(spellranks, 0, sizeof(spellranks)); memset(spellranks, 0, sizeof(spellranks));
if (was == DO_PRECOMBATSPELL) { if (rule_igjarjuk_curse && was == DO_PRECOMBATSPELL) {
summon_igjarjuk(b, spellranks); summon_igjarjuk(b, spellranks);
} }
for (s = b->sides; s != b->sides + b->nsides; ++s) { for (s = b->sides; s != b->sides + b->nsides; ++s) {
fighter *fig; fighter *fig;
for (fig = s->fighters; fig; fig = fig->next) { for (fig = s->fighters; fig; fig = fig->next) {
unit *mage = fig->unit; unit *mage = fig->unit;
unit *caster = mage;
if (fig->alive <= 0) if (fig->alive <= 0)
continue; /* fighter kann im Kampf get<65>tet worden sein */ continue; /* fighter kann im Kampf get<65>tet worden sein */
@ -1788,7 +1791,7 @@ void do_combatmagic(battle * b, combatmagic_t was)
continue; continue;
} }
level = eff_spelllevel(mage, sp, level, 1); level = eff_spelllevel(mage, caster, sp, level, 1);
if (sl > 0 && sl < level) { if (sl > 0 && sl < level) {
level = sl; level = sl;
} }
@ -1802,11 +1805,11 @@ void do_combatmagic(battle * b, combatmagic_t was)
free_order(ord); free_order(ord);
if (power <= 0) { /* Effekt von Antimagie */ if (power <= 0) { /* Effekt von Antimagie */
report_failed_spell(b, mage, sp); report_failed_spell(b, mage, sp);
pay_spell(mage, sp, level, 1); pay_spell(mage, NULL, sp, level, 1);
} }
else if (fumble(r, mage, sp, level)) { else if (fumble(r, mage, sp, level)) {
report_failed_spell(b, mage, sp); report_failed_spell(b, mage, sp);
pay_spell(mage, sp, level, 1); pay_spell(mage, NULL, sp, level, 1);
} }
else { else {
co = create_castorder_combat(0, fig, sp, level, power); co = create_castorder_combat(0, fig, sp, level, power);
@ -1822,7 +1825,7 @@ void do_combatmagic(battle * b, combatmagic_t was)
level = cast_spell(co); level = cast_spell(co);
if (level > 0) { if (level > 0) {
pay_spell(fig->unit, sp, level, 1); pay_spell(fig->unit, NULL, sp, level, 1);
} }
} }
} }
@ -1839,7 +1842,7 @@ static int cast_combatspell(troop at, const spell * sp, int level, double force)
level = cast_spell(&co); level = cast_spell(&co);
free_castorder(&co); free_castorder(&co);
if (level > 0) { if (level > 0) {
pay_spell(at.fighter->unit, sp, level, 1); pay_spell(at.fighter->unit, NULL, sp, level, 1);
} }
return level; return level;
} }
@ -1848,7 +1851,7 @@ static void do_combatspell(troop at)
{ {
const spell *sp; const spell *sp;
fighter *fi = at.fighter; fighter *fi = at.fighter;
unit *caster = fi->unit; unit *mage = fi->unit;
battle *b = fi->side->battle; battle *b = fi->side->battle;
region *r = b->region; region *r = b->region;
selist *ql; selist *ql;
@ -1857,28 +1860,28 @@ static void do_combatspell(troop at)
int fumblechance = 0; int fumblechance = 0;
order *ord; order *ord;
int sl; int sl;
const struct locale *lang = caster->faction->locale; const struct locale *lang = mage->faction->locale;
sp = get_combatspell(caster, 1); sp = get_combatspell(mage, 1);
if (sp == NULL) { if (sp == NULL) {
fi->magic = 0; /* Hat keinen Kampfzauber, k<>mpft nichtmagisch weiter */ fi->magic = 0; /* Hat keinen Kampfzauber, k<>mpft nichtmagisch weiter */
return; return;
} }
ord = create_order(K_CAST, lang, "'%s'", spell_name(sp, lang)); ord = create_order(K_CAST, lang, "'%s'", spell_name(sp, lang));
if (!cancast(caster, sp, 1, 1, ord)) { if (!cancast(mage, sp, 1, 1, ord)) {
fi->magic = 0; /* Kann nicht mehr Zaubern, k<>mpft nichtmagisch weiter */ fi->magic = 0; /* Kann nicht mehr Zaubern, k<>mpft nichtmagisch weiter */
return; return;
} }
level = eff_spelllevel(caster, sp, fi->magic, 1); level = eff_spelllevel(mage, mage, sp, fi->magic, 1);
sl = get_combatspelllevel(caster, 1); sl = get_combatspelllevel(mage, 1);
if (sl > 0 && sl < level) { if (sl > 0 && sl < level) {
level = sl; level = sl;
} }
if (fumble(r, caster, sp, level)) { if (fumble(r, mage, sp, level)) {
report_failed_spell(b, caster, sp); report_failed_spell(b, mage, sp);
pay_spell(caster, sp, level, 1); pay_spell(mage, NULL, sp, level, 1);
return; return;
} }
@ -1894,16 +1897,16 @@ static void do_combatspell(troop at)
/* Antimagie die Fehlschlag erh<72>ht */ /* Antimagie die Fehlschlag erh<72>ht */
if (rng_int() % 100 < fumblechance) { if (rng_int() % 100 < fumblechance) {
report_failed_spell(b, caster, sp); report_failed_spell(b, mage, sp);
pay_spell(caster, sp, level, 1); pay_spell(mage, NULL, sp, level, 1);
free_order(ord); free_order(ord);
return; return;
} }
power = spellpower(r, caster, sp, level, ord); power = spellpower(r, mage, sp, level, ord);
free_order(ord); free_order(ord);
if (power <= 0) { /* Effekt von Antimagie */ if (power <= 0) { /* Effekt von Antimagie */
report_failed_spell(b, caster, sp); report_failed_spell(b, mage, sp);
pay_spell(caster, sp, level, 1); pay_spell(mage, NULL, sp, level, 1);
return; return;
} }
@ -2076,6 +2079,7 @@ void dazzle(battle * b, troop * td)
void damage_building(battle * b, building * bldg, int damage_abs) void damage_building(battle * b, building * bldg, int damage_abs)
{ {
assert(bldg);
bldg->size = MAX(1, bldg->size - damage_abs); bldg->size = MAX(1, bldg->size - damage_abs);
/* Wenn Burg, dann gucken, ob die Leute alle noch in das Geb<65>ude passen. */ /* Wenn Burg, dann gucken, ob die Leute alle noch in das Geb<65>ude passen. */
@ -3425,11 +3429,12 @@ int join_battle(battle * b, unit * u, bool attack, fighter ** cp)
battle *make_battle(region * r) battle *make_battle(region * r)
{ {
battle *b = (battle *)calloc(1, sizeof(battle));
unit *u; unit *u;
bfaction *bf; bfaction *bf;
building * bld; building * bld;
battle *b = (battle *)calloc(1, sizeof(battle));
assert(b);
/* Alle Mann raus aus der Burg! */ /* Alle Mann raus aus der Burg! */
for (bld = r->buildings; bld != NULL; bld = bld->next) for (bld = r->buildings; bld != NULL; bld = bld->next)
bld->sizeleft = bld->size; bld->sizeleft = bld->size;
@ -3447,6 +3452,7 @@ battle *make_battle(region * r)
} }
if (!bf) { if (!bf) {
bf = (bfaction *)calloc(1, sizeof(bfaction)); bf = (bfaction *)calloc(1, sizeof(bfaction));
assert(bf);
++b->nfactions; ++b->nfactions;
bf->faction = u->faction; bf->faction = u->faction;
bf->next = b->factions; bf->next = b->factions;
@ -3470,14 +3476,15 @@ static void free_side(side * si)
static void free_fighter(fighter * fig) static void free_fighter(fighter * fig)
{ {
armor **ap = &fig->armors;
while (*ap) {
armor *a = *ap;
*ap = a->next;
free(a);
}
while (fig->loot) { while (fig->loot) {
i_free(i_remove(&fig->loot, fig->loot)); i_free(i_remove(&fig->loot, fig->loot));
} }
while (fig->armors) {
armor *a = fig->armors;
fig->armors = a->next;
free(a);
}
free(fig->person); free(fig->person);
free(fig->weapons); free(fig->weapons);
@ -3489,13 +3496,14 @@ static void battle_free(battle * b) {
assert(b); assert(b);
for (s = b->sides; s != b->sides + b->nsides; ++s) { for (s = b->sides; s != b->sides + b->nsides; ++s) {
fighter *fnext = s->fighters; fighter **fp = &s->fighters;
while (fnext) { while (*fp) {
fighter *fig = fnext; fighter *fig = *fp;
fnext = fig->next; *fp = fig->next;
free_fighter(fig); free_fighter(fig);
free(fig); free(fig);
} }
s->fighters = NULL;
free_side(s); free_side(s);
} }
free(b); free(b);

View File

@ -5,7 +5,6 @@
#include "bind_process.h" #include "bind_process.h"
#include "battle.h" #include "battle.h"
#include "contact.h"
#include "economy.h" #include "economy.h"
#include "laws.h" #include "laws.h"
#include "magic.h" #include "magic.h"

View File

@ -52,7 +52,6 @@
#include <util/language.h> #include <util/language.h>
#include <util/log.h> #include <util/log.h>
#include <util/macros.h> #include <util/macros.h>
#include <util/message.h>
#include <util/rand.h> #include <util/rand.h>
#include <util/rng.h> #include <util/rng.h>

View File

@ -607,7 +607,7 @@ struct spellbook * faction_get_spellbook(struct faction *f)
if (f->magiegebiet != M_GRAY) { if (f->magiegebiet != M_GRAY) {
return get_spellbook(magic_school[f->magiegebiet]); return get_spellbook(magic_school[f->magiegebiet]);
} }
return 0; return NULL;
} }
static int allied_skillcount(const faction * f, skill_t sk) static int allied_skillcount(const faction * f, skill_t sk)

View File

@ -39,8 +39,9 @@
#define FIXATKEYS_VERSION 361 /* remove global.attribs, fix at_keys */ #define FIXATKEYS_VERSION 361 /* remove global.attribs, fix at_keys */
#define FACTION_UID_VERSION 362 /* f->uid contains a database id */ #define FACTION_UID_VERSION 362 /* f->uid contains a database id */
#define CRYPT_VERSION 363 /* passwords are encrypted */ #define CRYPT_VERSION 363 /* passwords are encrypted */
#define FAMILIAR_FIXMAGE_VERSION 364 /* familiar links are fixed */
#define RELEASE_VERSION CRYPT_VERSION /* current datafile */ #define RELEASE_VERSION FAMILIAR_FIXMAGE_VERSION /* current datafile */
#define MIN_VERSION UIDHASH_VERSION /* minimal datafile we support */ #define MIN_VERSION UIDHASH_VERSION /* minimal datafile we support */
#define MAX_VERSION RELEASE_VERSION /* change this if we can need to read the future datafile, and we can do so */ #define MAX_VERSION RELEASE_VERSION /* change this if we can need to read the future datafile, and we can do so */

View File

@ -26,7 +26,6 @@ extern "C" {
#include <util/message.h> #include <util/message.h>
struct faction; struct faction;
struct msglevel;
typedef struct mlist { typedef struct mlist {
struct mlist *next; struct mlist *next;
@ -39,13 +38,6 @@ extern "C" {
void free_messagelist(struct mlist *msgs); void free_messagelist(struct mlist *msgs);
typedef struct msglevel {
/* used to set specialized msg-levels */
struct msglevel *next;
const struct message_type *type;
int level;
} msglevel;
#define MESSAGE_MISSING_IGNORE 0 #define MESSAGE_MISSING_IGNORE 0
#define MESSAGE_MISSING_ERROR 1 #define MESSAGE_MISSING_ERROR 1
#define MESSAGE_MISSING_REPLACE 2 #define MESSAGE_MISSING_REPLACE 2

View File

@ -1293,50 +1293,63 @@ ship *read_ship(gamedata *data)
return sh; return sh;
} }
static void fix_fam_mage(unit *u) {
sc_mage *m = get_mage(u);
if (m && m->magietyp != M_GRAY) {
/* unit should be a familiar that has aura and a spell-list */
if (!m->spellbook) {
m->magietyp = M_GRAY;
}
}
}
static void fix_familiars(void) { static void fix_fam_triggers(unit *u) {
attrib * a = a_find(u->attribs, &at_mage);
attrib * am = a_find(u->attribs, &at_familiarmage);
if (!am && a) {
/* not a familiar, but magical */
attrib * ae = a_find(u->attribs, &at_eventhandler);
if (ae) {
trigger **tlist;
tlist = get_triggers(ae, "destroy");
if (tlist) {
trigger *t;
unit *um = NULL;
for (t = *tlist; t; t = t->next) {
if (t->type == &tt_shock) {
um = (unit *)t->data.v;
break;
}
}
if (um) {
attrib *af = a_find(um->attribs, &at_familiar);
log_error("%s seems to be a broken familiar of %s.",
unitname(u), unitname(um));
if (af) {
unit * uf = (unit *)af->data.v;
log_error("%s already has a familiar: %s.",
unitname(um), unitname(uf));
}
else {
set_familiar(um, u);
}
}
else {
log_error("%s seems to be a broken familiar with no trigger.", unitname(u));
}
}
}
}
}
static void fix_familiars(void (*callback)(unit *)) {
region *r; region *r;
for (r = regions; r; r = r->next) { for (r = regions; r; r = r->next) {
unit * u; unit * u;
for (u = r->units; u; u = u->next) { for (u = r->units; u; u = u->next) {
if (u->_race != u->faction->race && (u->_race->flags & RCF_FAMILIAR)) { if (u->_race != u->faction->race && (u->_race->flags & RCF_FAMILIAR)) {
/* unit is potentially a familiar */ /* unit is potentially a familiar */
attrib * a = a_find(u->attribs, &at_mage); callback(u);
attrib * am = a_find(u->attribs, &at_familiarmage);
if (!am && a) {
/* not a familiar, but magical */
attrib * ae = a_find(u->attribs, &at_eventhandler);
if (ae) {
trigger **tlist;
tlist = get_triggers(ae, "destroy");
if (tlist) {
trigger *t;
unit *um = NULL;
for (t = *tlist; t; t = t->next) {
if (t->type == &tt_shock) {
um = (unit *)t->data.v;
break;
}
}
if (um) {
attrib *af = a_find(um->attribs, &at_familiar);
log_error("%s seems to be a broken familiar of %s.",
unitname(u), unitname(um));
if (af) {
unit * uf = (unit *)af->data.v;
log_error("%s already has a familiar: %s.",
unitname(um), unitname(uf));
}
else {
set_familiar(um, u);
}
}
else {
log_error("%s seems to be a broken familiar with no trigger.", unitname(u));
}
}
}
}
} }
} }
} }
@ -1524,7 +1537,10 @@ int read_game(gamedata *data)
} }
if (data->version < FAMILIAR_FIX_VERSION) { if (data->version < FAMILIAR_FIX_VERSION) {
fix_familiars(); fix_familiars(fix_fam_triggers);
}
if (data->version < FAMILIAR_FIXMAGE_VERSION) {
fix_familiars(fix_fam_mage);
} }
log_debug("Done loading turn %d.", turn); log_debug("Done loading turn %d.", turn);

View File

@ -208,6 +208,7 @@ static buddy *get_friends(const unit * u, int *numfriends)
nf = *fr; nf = *fr;
if (nf == NULL || nf->faction != u2->faction) { if (nf == NULL || nf->faction != u2->faction) {
nf = malloc(sizeof(buddy)); nf = malloc(sizeof(buddy));
assert(nf);
nf->next = *fr; nf->next = *fr;
nf->faction = u2->faction; nf->faction = u2->faction;
nf->unit = u2; nf->unit = u2;
@ -1004,11 +1005,15 @@ skill *add_skill(unit * u, skill_t sk)
skill *sv; skill *sv;
int i; int i;
assert(u);
for (i = 0; i != u->skill_size; ++i) { for (i = 0; i != u->skill_size; ++i) {
sv = u->skills + i; sv = u->skills + i;
if (sv->id >= sk) break; if (sv->id >= sk) break;
} }
u->skills = realloc(u->skills, (1 + u->skill_size) * sizeof(skill)); sv = realloc(u->skills, (1 + u->skill_size) * sizeof(skill));
assert(sv);
u->skills = sv;
sv = u->skills + i; sv = u->skills + i;
if (i < u->skill_size) { if (i < u->skill_size) {
assert(sv->id != sk); assert(sv->id != sk);
@ -1244,24 +1249,30 @@ int invisible(const unit * target, const unit * viewer)
*/ */
void free_unit(unit * u) void free_unit(unit * u)
{ {
struct reservation **pres = &u->reservations;
assert(!u->region); assert(!u->region);
free(u->_name); free(u->_name);
free_order(u->thisorder); free_order(u->thisorder);
free_orders(&u->orders); free_orders(&u->orders);
if (u->skills)
while (*pres) {
struct reservation *res = *pres;
*pres = res->next;
free(res);
}
if (u->skills) {
free(u->skills); free(u->skills);
u->skills = NULL;
}
while (u->items) { while (u->items) {
item *it = u->items->next; item *it = u->items->next;
u->items->next = NULL; u->items->next = NULL;
i_free(u->items); i_free(u->items);
u->items = it; u->items = it;
} }
while (u->attribs) while (u->attribs) {
a_remove(&u->attribs, u->attribs); a_remove(&u->attribs, u->attribs);
while (u->reservations) {
struct reservation *res = u->reservations;
u->reservations = res->next;
free(res);
} }
} }
@ -1671,7 +1682,7 @@ struct spellbook * unit_get_spellbook(const struct unit * u)
return faction_get_spellbook(u->faction); return faction_get_spellbook(u->faction);
} }
} }
return 0; return NULL;
} }
int effskill(const unit * u, skill_t sk, const region *r) int effskill(const unit * u, skill_t sk, const region *r)

View File

@ -657,8 +657,6 @@ int change_maxspellpoints(unit * u, int csp)
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/* Counter für die bereits gezauberte Anzahl Sprüche pro Runde. /* Counter für die bereits gezauberte Anzahl Sprüche pro Runde.
* Um nur die Zahl der bereits gezauberten Sprüche zu ermitteln mit
* step = 0 aufrufen.
*/ */
int countspells(unit * u, int step) int countspells(unit * u, int step)
{ {
@ -666,36 +664,60 @@ int countspells(unit * u, int step)
int count; int count;
m = get_mage_depr(u); m = get_mage_depr(u);
if (!m) if (!m) {
return 0; return 0;
}
if (step == 0) if (step == 0) {
return m->spellcount; return m->spellcount;
}
count = m->spellcount + step; count = m->spellcount + step;
m->spellcount = (count > 0) ? count : 0; m->spellcount = (count > 0) ? count : 0;
return m->spellcount; return m->spellcount;
} }
/* ------------------------------------------------------------- */ int spellcount(const unit *u) {
/* Die für den Spruch benötigte Aura pro Stufe. sc_mage *m = get_mage_depr(u);
* Die Grundkosten pro Stufe werden hier um 2^count erhöht. Der if (m) {
* Parameter count ist dabei die Anzahl der bereits gezauberten Sprüche return m->spellcount;
}
return 0;
}
/**
* Die Grundkosten pro Stufe werden um 2^count erhöht. countspells(u)
* ist dabei die Anzahl der bereits gezauberten Sprüche
*/ */
int spellcost(unit * u, const spell * sp) int aura_multiplier(const unit * u) {
int count = spellcount(u);
return (1 << count);
}
int spellcost(const unit * caster, const struct spell_component *spc)
{ {
int k, aura = 0;
int count = countspells(u, 0);
const resource_type *r_aura = get_resourcetype(R_AURA); const resource_type *r_aura = get_resourcetype(R_AURA);
for (k = 0; sp->components && sp->components[k].type; k++) { if (spc->type == r_aura) {
if (sp->components[k].type == r_aura) { return spc->amount * aura_multiplier(caster);
aura = sp->components[k].amount; }
return spc->amount;
}
/**
* Die für den Spruch benötigte Aura pro Stufe.
*/
int auracost(const unit *caster, const spell *sp) {
const resource_type *r_aura = get_resourcetype(R_AURA);
if (sp->components) {
int k;
for (k = 0; sp->components[k].type; ++k) {
const struct spell_component *spc = sp->components + k;
if (r_aura == spc->type) {
return spc->amount * aura_multiplier(caster);
}
} }
} }
aura *= (1 << count); return 0;
return aura;
} }
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
@ -726,17 +748,17 @@ static int spl_costtyp(const spell * sp)
return costtyp; return costtyp;
} }
/* ------------------------------------------------------------- */ /**
/* durch Komponenten und cast_level begrenzter maximal möglicher * Durch Komponenten und cast_level begrenzter maximal möglicherLevel.
* Level *
* Da die Funktion nicht alle Komponenten durchprobiert sondern beim * Da die Funktion nicht alle Komponenten durchprobiert sondern beim
* ersten Fehler abbricht, muss die Fehlermeldung später mit cancast() * ersten Fehler abbricht, muss die Fehlermeldung später mit cancast()
* generiert werden. * generiert werden.
* */ */
int eff_spelllevel(unit * u, const spell * sp, int cast_level, int range) int eff_spelllevel(unit * u, unit *caster, const spell * sp, int cast_level, int range)
{ {
const resource_type *r_aura = get_resourcetype(R_AURA); const resource_type *r_aura = get_resourcetype(R_AURA);
int k, maxlevel, needplevel; int k, maxlevel;
int costtyp = SPC_FIX; int costtyp = SPC_FIX;
for (k = 0; sp->components && sp->components[k].type; k++) { for (k = 0; sp->components && sp->components[k].type; k++) {
@ -744,17 +766,15 @@ int eff_spelllevel(unit * u, const spell * sp, int cast_level, int range)
return 0; return 0;
if (sp->components[k].amount > 0) { if (sp->components[k].amount > 0) {
/* Die Kosten für Aura sind auch von der Zahl der bereits int level_cost = sp->components[k].amount * range;
* gezauberten Sprüche abhängig */
if (sp->components[k].type == r_aura) { if (sp->components[k].type == r_aura) {
needplevel = spellcost(u, sp) * range; /* Die Kosten fuer Aura sind auch von der Zahl der bereits
} * gezauberten Sprueche abhaengig */
else { level_cost *= aura_multiplier(caster);
needplevel = sp->components[k].amount * range;
} }
maxlevel = maxlevel =
get_pooled(u, sp->components[k].type, GET_DEFAULT, get_pooled(u, sp->components[k].type, GET_DEFAULT,
needplevel * cast_level) / needplevel; level_cost * cast_level) / level_cost;
/* sind die Kosten fix, so muss die Komponente nur einmal vorhanden /* sind die Kosten fix, so muss die Komponente nur einmal vorhanden
* sein und der cast_level ändert sich nicht */ * sein und der cast_level ändert sich nicht */
@ -803,27 +823,20 @@ int eff_spelllevel(unit * u, const spell * sp, int cast_level, int range)
* Je nach Kostenart werden dann die Komponenten noch mit cast_level * Je nach Kostenart werden dann die Komponenten noch mit cast_level
* multipliziert. * multipliziert.
*/ */
void pay_spell(unit * u, const spell * sp, int cast_level, int range) void pay_spell(unit * mage, const unit *caster, const spell * sp, int cast_level, int range)
{ {
const resource_type *r_aura = get_resourcetype(R_AURA);
int k; int k;
int resuse;
assert(cast_level > 0); assert(cast_level > 0);
for (k = 0; sp->components && sp->components[k].type; k++) { for (k = 0; sp->components && sp->components[k].type; k++) {
if (sp->components[k].type == r_aura) { const struct spell_component *spc = sp->components + k;
resuse = spellcost(u, sp) * range; int resuse = spellcost(caster ? caster : mage, spc) * range;
}
else {
resuse = sp->components[k].amount * range;
}
if (sp->components[k].cost == SPC_LINEAR if (spc->cost == SPC_LINEAR || spc->cost == SPC_LEVEL) {
|| sp->components[k].cost == SPC_LEVEL) {
resuse *= cast_level; resuse *= cast_level;
} }
use_pooled(u, sp->components[k].type, GET_DEFAULT, resuse); use_pooled(mage, spc->type, GET_DEFAULT, resuse);
} }
} }
@ -851,9 +864,7 @@ bool knowsspell(const region * r, const unit * u, const spell * sp)
bool bool
cancast(unit * u, const spell * sp, int level, int range, struct order * ord) cancast(unit * u, const spell * sp, int level, int range, struct order * ord)
{ {
const resource_type *r_aura = get_resourcetype(R_AURA);
int k; int k;
int itemanz;
resource *reslist = NULL; resource *reslist = NULL;
if (!knowsspell(u->region, u, sp)) { if (!knowsspell(u->region, u, sp)) {
@ -869,22 +880,18 @@ cancast(unit * u, const spell * sp, int level, int range, struct order * ord)
} }
for (k = 0; sp->components && sp->components[k].type; ++k) { for (k = 0; sp->components && sp->components[k].type; ++k) {
if (sp->components[k].amount > 0) { const struct spell_component *spc = sp->components + k;
const resource_type *rtype = sp->components[k].type; if (spc->amount > 0) {
int itemhave; const resource_type *rtype = spc->type;
int itemhave, itemanz;
/* Die Kosten für Aura sind auch von der Zahl der bereits /* Die Kosten für Aura sind auch von der Zahl der bereits
* gezauberten Sprüche abhängig */ * gezauberten Sprüche abhängig */
if (rtype == r_aura) { itemanz = spellcost(u, spc) * range;
itemanz = spellcost(u, sp) * range;
}
else {
itemanz = sp->components[k].amount * range;
}
/* sind die Kosten stufenabhängig, so muss itemanz noch mit dem /* sind die Kosten stufenabhängig, so muss itemanz noch mit dem
* level multipliziert werden */ * level multipliziert werden */
switch (sp->components[k].cost) { switch (spc->cost) {
case SPC_LEVEL: case SPC_LEVEL:
case SPC_LINEAR: case SPC_LINEAR:
itemanz *= level; itemanz *= level;
@ -897,6 +904,7 @@ cancast(unit * u, const spell * sp, int level, int range, struct order * ord)
itemhave = get_pooled(u, rtype, GET_DEFAULT, itemanz); itemhave = get_pooled(u, rtype, GET_DEFAULT, itemanz);
if (itemhave < itemanz) { if (itemhave < itemanz) {
resource *res = malloc(sizeof(resource)); resource *res = malloc(sizeof(resource));
assert(res);
res->number = itemanz - itemhave; res->number = itemanz - itemhave;
res->type = rtype; res->type = rtype;
res->next = reslist; res->next = reslist;
@ -1254,8 +1262,8 @@ bool fumble(region * r, unit * u, const spell * sp, int cast_grade)
} }
/* CHAOSPATZERCHANCE 10 : +10% Chance zu Patzern */ /* CHAOSPATZERCHANCE 10 : +10% Chance zu Patzern */
mage = get_mage_depr(u); mage = get_mage(u);
if (mage->magietyp == M_DRAIG) { if (mage && mage->magietyp == M_DRAIG) {
fumble_chance += CHAOSPATZERCHANCE; fumble_chance += CHAOSPATZERCHANCE;
} }
if (is_cursed(u->attribs, &ct_magicboost)) { if (is_cursed(u->attribs, &ct_magicboost)) {
@ -1276,11 +1284,11 @@ bool fumble(region * r, unit * u, const spell * sp, int cast_grade)
} }
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/* Dummy-Zauberpatzer, Platzhalter für speziel auf die Sprüche /* Dummy-Zauberpatzer, Platzhalter für speziell auf die Sprüche
* zugeschnittene Patzer */ * zugeschnittene Patzer */
static void fumble_default(castorder * co) static void fumble_default(castorder * co)
{ {
unit *mage = co->magician.u; unit *mage = co_get_magician(co);
cmistake(mage, co->order, 180, MSG_MAGIC); cmistake(mage, co->order, 180, MSG_MAGIC);
@ -1295,7 +1303,8 @@ static void do_fumble(castorder * co)
{ {
curse *c; curse *c;
region *r = co_get_region(co); region *r = co_get_region(co);
unit *u = co->magician.u; unit *mage = co_get_magician(co);
unit *caster = co_get_caster(co);
const spell *sp = co->sp; const spell *sp = co->sp;
int level = co->level; int level = co->level;
int duration; int duration;
@ -1304,8 +1313,8 @@ static void do_fumble(castorder * co)
static int rc_cache; static int rc_cache;
fumble_f fun; fumble_f fun;
ADDMSG(&u->faction->msgs, ADDMSG(&mage->faction->msgs,
msg_message("patzer", "unit region spell", u, r, sp)); msg_message("patzer", "unit region spell", mage, r, sp));
switch (rng_int() % 10) { switch (rng_int() % 10) {
case 0: case 0:
/* wenn vorhanden spezieller Patzer, ansonsten nix */ /* wenn vorhanden spezieller Patzer, ansonsten nix */
@ -1325,22 +1334,22 @@ static void do_fumble(castorder * co)
* The list of things to happen are attached to a timeout * The list of things to happen are attached to a timeout
* trigger and that's added to the triggerlit of the mage gone toad. * trigger and that's added to the triggerlit of the mage gone toad.
*/ */
trigger *trestore = trigger_changerace(u, u_race(u), u->irace); trigger *trestore = trigger_changerace(mage, u_race(mage), mage->irace);
if (chance(0.7)) { if (chance(0.7)) {
const resource_type *rtype = rt_find("toadslime"); const resource_type *rtype = rt_find("toadslime");
if (rtype) { if (rtype) {
t_add(&trestore, trigger_giveitem(u, rtype->itype, 1)); t_add(&trestore, trigger_giveitem(mage, rtype->itype, 1));
} }
} }
duration = rng_int() % level / 2; duration = rng_int() % level / 2;
if (duration < 2) duration = 2; if (duration < 2) duration = 2;
add_trigger(&u->attribs, "timer", trigger_timeout(duration, trestore)); add_trigger(&mage->attribs, "timer", trigger_timeout(duration, trestore));
if (rc_changed(&rc_cache)) { if (rc_changed(&rc_cache)) {
rc_toad = get_race(RC_TOAD); rc_toad = get_race(RC_TOAD);
} }
u_setrace(u, rc_toad); u_setrace(mage, rc_toad);
u->irace = NULL; mage->irace = NULL;
ADDMSG(&r->msgs, msg_message("patzer6", "unit region spell", u, r, sp)); ADDMSG(&r->msgs, msg_message("patzer6", "unit region spell", mage, r, sp));
break; break;
} }
/* fall-through is intentional! */ /* fall-through is intentional! */
@ -1350,26 +1359,26 @@ static void do_fumble(castorder * co)
duration = rng_int() % level / 2; duration = rng_int() % level / 2;
if (duration < 2) duration = 2; if (duration < 2) duration = 2;
effect = level / -2.0; effect = level / -2.0;
c = create_curse(u, &u->attribs, &ct_skillmod, level, c = create_curse(mage, &mage->attribs, &ct_skillmod, level,
duration, effect, 1); duration, effect, 1);
c->data.i = SK_MAGIC; c->data.i = SK_MAGIC;
ADDMSG(&u->faction->msgs, msg_message("patzer2", "unit region", u, r)); ADDMSG(&mage->faction->msgs, msg_message("patzer2", "unit region", mage, r));
break; break;
case 3: case 3:
case 4: case 4:
/* Spruch schlägt fehl, alle Magiepunkte weg */ /* Spruch schlägt fehl, alle Magiepunkte weg */
set_spellpoints(u, 0); set_spellpoints(mage, 0);
ADDMSG(&u->faction->msgs, msg_message("patzer3", "unit region spell", ADDMSG(&mage->faction->msgs, msg_message("patzer3", "unit region spell",
u, r, sp)); mage, r, sp));
break; break;
case 5: case 5:
case 6: case 6:
/* Spruch gelingt, aber alle Magiepunkte weg */ /* Spruch gelingt, aber alle Magiepunkte weg */
co->level = cast_spell(co); co->level = cast_spell(co);
set_spellpoints(u, 0); set_spellpoints(mage, 0);
ADDMSG(&u->faction->msgs, msg_message("patzer4", "unit region spell", ADDMSG(&mage->faction->msgs, msg_message("patzer4", "unit region spell",
u, r, sp)); mage, r, sp));
break; break;
case 7: case 7:
@ -1378,9 +1387,9 @@ static void do_fumble(castorder * co)
default: default:
/* Spruch gelingt, alle nachfolgenden Sprüche werden 2^4 so teuer */ /* Spruch gelingt, alle nachfolgenden Sprüche werden 2^4 so teuer */
co->level = cast_spell(co); co->level = cast_spell(co);
ADDMSG(&u->faction->msgs, msg_message("patzer5", "unit region spell", ADDMSG(&mage->faction->msgs, msg_message("patzer5", "unit region spell",
u, r, sp)); mage, r, sp));
countspells(u, 3); countspells(caster, 3);
} }
} }
@ -1591,7 +1600,7 @@ verify_unit(region * r, unit * mage, const spell * sp, spllprm * spobj,
static void static void
verify_targets(castorder * co, int *invalid, int *resist, int *success) verify_targets(castorder * co, int *invalid, int *resist, int *success)
{ {
unit *mage = co->magician.u; unit *mage = co_get_magician(co);
const spell *sp = co->sp; const spell *sp = co->sp;
region *target_r = co_get_region(co); region *target_r = co_get_region(co);
spellparameter *sa = co->par; spellparameter *sa = co->par;
@ -2038,6 +2047,10 @@ struct unit * co_get_caster(const struct castorder * co) {
return co->_familiar ? co->_familiar : co->magician.u; return co->_familiar ? co->_familiar : co->magician.u;
} }
struct unit * co_get_magician(const struct castorder * co) {
return co->magician.u;
}
struct region * co_get_region(const struct castorder * co) { struct region * co_get_region(const struct castorder * co) {
return co->_rtarget; return co->_rtarget;
} }
@ -2432,7 +2445,7 @@ static castorder *cast_cmd(unit * u, order * ord)
spell *sp = 0; spell *sp = 0;
plane *pl; plane *pl;
spellparameter *args = NULL; spellparameter *args = NULL;
unit * caster = u; unit * mage = u;
param_t param; param_t param;
if (LongHunger(u)) { if (LongHunger(u)) {
@ -2502,15 +2515,15 @@ static castorder *cast_cmd(unit * u, order * ord)
* können. unit_getspell findet aber nur jene Sprüche, die * können. unit_getspell findet aber nur jene Sprüche, die
* die Einheit beherrscht. */ * die Einheit beherrscht. */
if (!sp && is_familiar(u)) { if (!sp && is_familiar(u)) {
caster = get_familiar_mage(u); mage = get_familiar_mage(u);
if (caster) { if (mage) {
familiar = u; familiar = u;
sp = unit_getspell(caster, s, caster->faction->locale); sp = unit_getspell(mage, s, mage->faction->locale);
} }
else { else {
/* somehow, this familiar has no mage! */ /* somehow, this familiar has no mage! */
log_error("cast_cmd: familiar %s is without a mage?\n", unitname(u)); log_error("cast_cmd: familiar %s is without a mage?\n", unitname(u));
caster = u; mage = u;
} }
} }
@ -2597,24 +2610,22 @@ static castorder *cast_cmd(unit * u, order * ord)
cmistake(u, ord, 177, MSG_MAGIC); cmistake(u, ord, 177, MSG_MAGIC);
return 0; return 0;
} }
if (caster != familiar) { /* Magier zaubert durch Vertrauten */ if (mage != familiar) { /* Magier zaubert durch Vertrauten */
int sk; int sk;
if (range > 1) { /* Fehler! Versucht zu Farcasten */ if (range > 1) { /* Fehler! Versucht zu Farcasten */
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "familiar_farcast", ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "familiar_farcast",
"mage", caster)); "mage", mage));
return 0; return 0;
} }
sk = effskill(caster, SK_MAGIC, 0); sk = effskill(mage, SK_MAGIC, 0);
if (distance(caster->region, r) > sk) { if (distance(mage->region, r) > sk) {
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "familiar_toofar", ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "familiar_toofar",
"mage", caster)); "mage", mage));
return 0; return 0;
} }
/* mage auf magier setzen, level anpassen, range für Erhöhung /* mage auf magier setzen, level anpassen, range für Erhöhung
* der Spruchkosten nutzen, langen Befehl des Magiers * der Spruchkosten nutzen */
* löschen, zaubern kann er noch */
range *= 2; range *= 2;
set_order(&caster->thisorder, NULL);
sk /= 2; sk /= 2;
if (level > sk) level = sk; if (level > sk) level = sk;
} }
@ -2635,7 +2646,7 @@ static castorder *cast_cmd(unit * u, order * ord)
unitname(u), MAX_PARAMETERS, sp->sname); unitname(u), MAX_PARAMETERS, sp->sname);
} }
args = args =
add_spellparameter(target_r, caster, sp->parameter, add_spellparameter(target_r, mage, sp->parameter,
(const char *const *)params, p, ord); (const char *const *)params, p, ord);
for (i = 0; i != p; ++i) { for (i = 0; i != p; ++i) {
free(params[i]); free(params[i]);
@ -2645,7 +2656,7 @@ static castorder *cast_cmd(unit * u, order * ord)
return 0; return 0;
} }
} }
return create_castorder(0, caster, familiar, sp, target_r, level, 0, range, ord, return create_castorder(0, mage, familiar, sp, target_r, level, 0, range, ord,
args); args);
} }
@ -2718,16 +2729,17 @@ void magic(void)
order *ord = co->order; order *ord = co->order;
int invalid, resist, success, cast_level = co->level; int invalid, resist, success, cast_level = co->level;
bool fumbled = false; bool fumbled = false;
unit *u = co->magician.u; unit *mage = co_get_magician(co);
unit *caster = co_get_caster(co);
const spell *sp = co->sp; const spell *sp = co->sp;
region *target_r = co_get_region(co); region *target_r = co_get_region(co);
/* reichen die Komponenten nicht, wird der Level reduziert. */ /* reichen die Komponenten nicht, wird der Level reduziert. */
co->level = eff_spelllevel(u, sp, cast_level, co->distance); co->level = eff_spelllevel(mage, caster, sp, cast_level, co->distance);
if (co->level < 1) { if (co->level < 1) {
/* Fehlermeldung mit Komponenten generieren */ /* Fehlermeldung mit Komponenten generieren */
cancast(u, sp, cast_level, co->distance, ord); cancast(mage, sp, cast_level, co->distance, ord);
continue; continue;
} }
@ -2735,24 +2747,24 @@ void magic(void)
/* Sprüche mit Fixkosten werden immer auf Stufe des Spruchs /* Sprüche mit Fixkosten werden immer auf Stufe des Spruchs
* gezaubert, co->level ist aber defaultmäßig Stufe des Magiers */ * gezaubert, co->level ist aber defaultmäßig Stufe des Magiers */
if (spl_costtyp(sp) != SPC_FIX) { if (spl_costtyp(sp) != SPC_FIX) {
ADDMSG(&u->faction->msgs, msg_message("missing_components", ADDMSG(&mage->faction->msgs, msg_message("missing_components",
"unit spell level", u, sp, cast_level)); "unit spell level", mage, sp, cast_level));
} }
} }
/* Prüfen, ob die realen Kosten für die gewünschten Stufe bezahlt /* Prüfen, ob die realen Kosten für die gewünschten Stufe bezahlt
* werden können */ * werden können */
if (!cancast(u, sp, co->level, co->distance, ord)) { if (!cancast(mage, sp, co->level, co->distance, ord)) {
/* die Fehlermeldung wird in cancast generiert */ /* die Fehlermeldung wird in cancast generiert */
continue; continue;
} }
co->force = MagicPower(spellpower(target_r, u, sp, co->level, ord)); co->force = MagicPower(spellpower(target_r, mage, sp, co->level, ord));
/* die Stärke kann durch Antimagie auf 0 sinken */ /* die Stärke kann durch Antimagie auf 0 sinken */
if (co->force <= 0) { if (co->force <= 0) {
co->force = 0; co->force = 0;
ADDMSG(&u->faction->msgs, msg_message("missing_force", ADDMSG(&mage->faction->msgs, msg_message("missing_force",
"unit spell level", u, sp, co->level)); "unit spell level", mage, sp, co->level));
} }
/* Ziele auf Existenz prüfen und Magieresistenz feststellen. Wurde /* Ziele auf Existenz prüfen und Magieresistenz feststellen. Wurde
@ -2772,15 +2784,15 @@ void magic(void)
co->force = 0; co->force = 0;
/* zwar wurde mindestens ein Ziel gefunden, das widerstand /* zwar wurde mindestens ein Ziel gefunden, das widerstand
* jedoch dem Zauber. Kosten abziehen und abbrechen. */ * jedoch dem Zauber. Kosten abziehen und abbrechen. */
ADDMSG(&u->faction->msgs, msg_message("spell_resist", ADDMSG(&mage->faction->msgs, msg_message("spell_resist",
"unit region spell", u, r, sp)); "unit region spell", mage, r, sp));
} }
} }
/* Auch für Patzer gibt es Erfahrung, müssen die Spruchkosten /* Auch für Patzer gibt es Erfahrung, müssen die Spruchkosten
* bezahlt werden und die nachfolgenden Sprüche werden teurer */ * bezahlt werden und die nachfolgenden Sprüche werden teurer */
if (co->force > 0) { if (co->force > 0) {
if (fumble(target_r, u, sp, co->level)) { if (fumble(target_r, mage, sp, co->level)) {
/* zuerst bezahlen, dann evt in do_fumble alle Aura verlieren */ /* zuerst bezahlen, dann evt in do_fumble alle Aura verlieren */
fumbled = true; fumbled = true;
} }
@ -2794,12 +2806,12 @@ void magic(void)
} }
/* erst bezahlen, dann Kostenzähler erhöhen */ /* erst bezahlen, dann Kostenzähler erhöhen */
if (co->level > 0) { if (co->level > 0) {
pay_spell(u, sp, co->level, co->distance); pay_spell(mage, caster, sp, co->level, co->distance);
} }
if (fumbled) { if (fumbled) {
do_fumble(co); do_fumble(co);
} }
countspells(u, 1); countspells(caster, 1);
} }
} }
@ -2807,9 +2819,9 @@ void magic(void)
for (r = regions; r; r = r->next) { for (r = regions; r; r = r->next) {
unit *u; unit *u;
for (u = r->units; u; u = u->next) { for (u = r->units; u; u = u->next) {
if (is_mage(u) && countspells(u, 0) > 0) { if (is_mage(u) && spellcount(u) > 0) {
produceexp(u, SK_MAGIC, u->number); produceexp(u, SK_MAGIC, u->number);
/* Spruchlistenaktualiesierung ist in Regeneration */ /* Spruchlistenaktualisierung ist in Regeneration */
} }
} }
} }
@ -2859,33 +2871,22 @@ static void select_spellbook(void **tokens, spellbook *sb, const struct locale *
spell *unit_getspell(struct unit *u, const char *name, const struct locale * lang) spell *unit_getspell(struct unit *u, const char *name, const struct locale * lang)
{ {
void * tokens = 0;
spellbook *sb; spellbook *sb;
sb = unit_get_spellbook(u); sb = unit_get_spellbook(u);
if (sb) { if (sb) {
void * tokens = NULL;
select_spellbook(&tokens, sb, lang); select_spellbook(&tokens, sb, lang);
} if (tokens) {
#if 0 /* TODO: some familiars can cast spells from the mage's spellbook? */ variant token;
u = get_familiar_mage(u); if (findtoken(tokens, name, &token) != E_TOK_NOMATCH) {
if (u) { freetokens(tokens);
sb = unit_get_spellbook(u); return (spell *)token.v;
if (sb) { }
select_spellbook(&tokens, sb, lang);
}
}
#endif
if (tokens) {
variant token;
if (findtoken(tokens, name, &token) != E_TOK_NOMATCH) {
freetokens(tokens); freetokens(tokens);
return (spell *)token.v;
} }
freetokens(tokens);
} }
return NULL;
return 0;
} }
int cast_spell(struct castorder *co) int cast_spell(struct castorder *co)

View File

@ -140,6 +140,7 @@ extern "C" {
} castorder; } castorder;
struct unit * co_get_caster(const struct castorder * co); struct unit * co_get_caster(const struct castorder * co);
struct unit * co_get_magician(const struct castorder * co);
struct region * co_get_region(const struct castorder * co); struct region * co_get_region(const struct castorder * co);
typedef struct spell_component { typedef struct spell_component {
@ -288,9 +289,11 @@ extern "C" {
/* Prüfroutinen für Zaubern */ /* Prüfroutinen für Zaubern */
int countspells(struct unit *u, int step); int countspells(struct unit *u, int step);
int spellcount(const struct unit *u);
/* erhöht den Counter für Zaubersprüche um 'step' und gibt die neue /* erhöht den Counter für Zaubersprüche um 'step' und gibt die neue
* Anzahl der gezauberten Sprüche zurück. */ * Anzahl der gezauberten Sprüche zurück. */
int spellcost(struct unit *u, const struct spell * sp); int auracost(const struct unit *caster, const struct spell *sp);
int spellcost(const struct unit *caster, const struct spell_component *spc);
/* gibt die für diesen Spruch derzeit notwendigen Magiepunkte auf der /* gibt die für diesen Spruch derzeit notwendigen Magiepunkte auf der
* geringstmöglichen Stufe zurück, schon um den Faktor der bereits * geringstmöglichen Stufe zurück, schon um den Faktor der bereits
* zuvor gezauberten Sprüche erhöht */ * zuvor gezauberten Sprüche erhöht */
@ -298,12 +301,12 @@ extern "C" {
int distance, struct order *ord); int distance, struct order *ord);
/* true, wenn Einheit alle Komponenten des Zaubers (incl. MP) für die /* true, wenn Einheit alle Komponenten des Zaubers (incl. MP) für die
* geringstmögliche Stufe hat und den Spruch beherrscht */ * geringstmögliche Stufe hat und den Spruch beherrscht */
void pay_spell(struct unit *u, const struct spell * sp, int eff_stufe, int distance); void pay_spell(struct unit *mage, const struct unit *caster, const struct spell * sp, int eff_stufe, int distance);
/* zieht die Komponenten des Zaubers aus dem Inventory der Einheit /* zieht die Komponenten des Zaubers aus dem Inventory der Einheit
* ab. Die effektive Stufe des gezauberten Spruchs ist wichtig für * ab. Die effektive Stufe des gezauberten Spruchs ist wichtig für
* die korrekte Bestimmung der Magiepunktkosten */ * die korrekte Bestimmung der Magiepunktkosten */
int eff_spelllevel(struct unit *u, const struct spell * sp, int cast_level, int eff_spelllevel(struct unit *mage, struct unit *caster,
int distance); const struct spell * sp, int cast_level, int distance);
/* ermittelt die effektive Stufe des Zaubers. Dabei ist cast_level /* ermittelt die effektive Stufe des Zaubers. Dabei ist cast_level
* die gewünschte maximale Stufe (im Normalfall Stufe des Magiers, * die gewünschte maximale Stufe (im Normalfall Stufe des Magiers,
* bei Farcasting Stufe*2^Entfernung) */ * bei Farcasting Stufe*2^Entfernung) */

View File

@ -123,9 +123,9 @@ void test_pay_spell(CuTest * tc)
change_resource(u, get_resourcetype(R_AURA), 3); change_resource(u, get_resourcetype(R_AURA), 3);
change_resource(u, get_resourcetype(R_HORSE), 3); change_resource(u, get_resourcetype(R_HORSE), 3);
level = eff_spelllevel(u, sp, 3, 1); level = eff_spelllevel(u, u, sp, 3, 1);
CuAssertIntEquals(tc, 3, level); CuAssertIntEquals(tc, 3, level);
pay_spell(u, sp, level, 1); pay_spell(u, NULL, sp, level, 1);
CuAssertIntEquals(tc, 0, get_resource(u, get_resourcetype(R_SILVER))); CuAssertIntEquals(tc, 0, get_resource(u, get_resourcetype(R_SILVER)));
CuAssertIntEquals(tc, 0, get_resource(u, get_resourcetype(R_AURA))); CuAssertIntEquals(tc, 0, get_resource(u, get_resourcetype(R_AURA)));
CuAssertIntEquals(tc, 0, get_resource(u, get_resourcetype(R_HORSE))); CuAssertIntEquals(tc, 0, get_resource(u, get_resourcetype(R_HORSE)));
@ -157,16 +157,16 @@ void test_pay_spell_failure(CuTest * tc)
CuAssertIntEquals(tc, 2, change_resource(u, get_resourcetype(R_AURA), 2)); CuAssertIntEquals(tc, 2, change_resource(u, get_resourcetype(R_AURA), 2));
CuAssertIntEquals(tc, 3, change_resource(u, get_resourcetype(R_HORSE), 3)); CuAssertIntEquals(tc, 3, change_resource(u, get_resourcetype(R_HORSE), 3));
level = eff_spelllevel(u, sp, 3, 1); level = eff_spelllevel(u, u, sp, 3, 1);
CuAssertIntEquals(tc, 2, level); CuAssertIntEquals(tc, 2, level);
pay_spell(u, sp, level, 1); pay_spell(u, NULL, sp, level, 1);
CuAssertIntEquals(tc, 1, change_resource(u, get_resourcetype(R_SILVER), 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, 3, change_resource(u, get_resourcetype(R_AURA), 3));
CuAssertIntEquals(tc, 2, change_resource(u, get_resourcetype(R_HORSE), 1)); CuAssertIntEquals(tc, 2, change_resource(u, get_resourcetype(R_HORSE), 1));
CuAssertIntEquals(tc, 0, eff_spelllevel(u, sp, 3, 1)); CuAssertIntEquals(tc, 0, eff_spelllevel(u, u, sp, 3, 1));
CuAssertIntEquals(tc, 0, change_resource(u, get_resourcetype(R_SILVER), -1)); CuAssertIntEquals(tc, 0, change_resource(u, get_resourcetype(R_SILVER), -1));
CuAssertIntEquals(tc, 0, eff_spelllevel(u, sp, 2, 1)); CuAssertIntEquals(tc, 0, eff_spelllevel(u, u, sp, 2, 1));
test_teardown(); test_teardown();
} }

File diff suppressed because it is too large Load Diff

View File

@ -38,7 +38,7 @@ int sp_flying_ship(castorder * co)
ship *sh; ship *sh;
unit *u; unit *u;
region *r; region *r;
unit *mage; unit *caster;
int cast_level; int cast_level;
double power; double power;
spellparameter *pa; spellparameter *pa;
@ -47,7 +47,7 @@ int sp_flying_ship(castorder * co)
assert(co); assert(co);
r = co_get_region(co); r = co_get_region(co);
mage = co->magician.u; caster = co_get_caster(co);
cast_level = co->level; cast_level = co->level;
power = co->force; power = co->force;
pa = co->par; pa = co->par;
@ -57,22 +57,22 @@ int sp_flying_ship(castorder * co)
return 0; return 0;
sh = pa->param[0]->data.sh; sh = pa->param[0]->data.sh;
if (sh->type->construction->maxsize > 50) { if (sh->type->construction->maxsize > 50) {
ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, ADDMSG(&caster->faction->msgs, msg_feedback(caster, co->order,
"error_flying_ship_too_big", "ship", sh)); "error_flying_ship_too_big", "ship", sh));
return 0; return 0;
} }
/* Duration = 1, nur diese Runde */ /* Duration = 1, nur diese Runde */
cno = levitate_ship(sh, mage, power, 1); cno = levitate_ship(sh, caster, power, 1);
if (cno == 0) { if (cno == 0) {
if (is_cursed(sh->attribs, &ct_flyingship)) { if (is_cursed(sh->attribs, &ct_flyingship)) {
/* Auf dem Schiff befindet liegt bereits so ein Zauber. */ /* Auf dem Schiff befindet liegt bereits so ein Zauber. */
cmistake(mage, co->order, 211, MSG_MAGIC); cmistake(caster, co->order, 211, MSG_MAGIC);
} }
else if (is_cursed(sh->attribs, &ct_shipspeedup)) { else if (is_cursed(sh->attribs, &ct_shipspeedup)) {
/* Es ist zu gefaehrlich, ein sturmgepeitschtes Schiff fliegen zu lassen. */ /* Es ist zu gefaehrlich, ein sturmgepeitschtes Schiff fliegen zu lassen. */
cmistake(mage, co->order, 210, MSG_MAGIC); cmistake(caster, co->order, 210, MSG_MAGIC);
} }
return 0; return 0;
} }
@ -86,7 +86,7 @@ int sp_flying_ship(castorder * co)
if (!(u->faction->flags & FFL_SELECT)) { if (!(u->faction->flags & FFL_SELECT)) {
u->faction->flags |= FFL_SELECT; u->faction->flags |= FFL_SELECT;
if (!m) { if (!m) {
m = msg_message("flying_ship_result", "mage ship", mage, sh); m = msg_message("flying_ship_result", "mage ship", caster, sh);
} }
add_message(&u->faction->msgs, m); add_message(&u->faction->msgs, m);
} }

View File

@ -784,13 +784,7 @@ int study_cmd(unit * u, order * ord)
show_potions(f, skill); show_potions(f, skill);
} }
} }
else if (sk == SK_MAGIC) { init_order(NULL, NULL);
sc_mage *mage = get_mage_depr(u);
if (!mage) {
mage = create_mage(u, u->faction->magiegebiet);
}
}
init_order_depr(NULL);
return 0; return 0;
} }

View File

@ -51,6 +51,7 @@ void register_special_direction(struct locale *lang, const char *name)
if (lang == locales) { if (lang == locales) {
dir_lookup *dl = malloc(sizeof(dir_lookup)); dir_lookup *dl = malloc(sizeof(dir_lookup));
assert(dl);
dl->name = str; dl->name = str;
dl->oldname = token; dl->oldname = token;
dl->next = dir_name_lookup; dl->next = dir_name_lookup;