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 u1 = unit.create(f, r1, 1)
local u2 = unit.create(f, r1, 1)
u2.name = 'Xolgrim'
u1:add_item("money", 100)
u2:add_item("money", 100)
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.peasants.growth.factor", "0")
eressea.settings.set("magic.fumble.enable", "0")
eressea.settings.set("magic.regeneration.enable", "0")
end
function test_shapeshift()
@ -104,6 +105,52 @@ function test_earn_silver()
assert_equal(0, r:get_resource("money"))
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()
local r = region.create(0, 0, "mountain")
local f = faction.create("human")
@ -148,3 +195,55 @@ function test_bug_2480()
process_orders()
assert_equal(0, u1.number);
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;
ptype = (potion_type *)calloc(1, sizeof(potion_type));
assert(ptype);
itype->flags |= ITF_POTION;
ptype->itype = itype;
ptype->level = level;

View File

@ -134,6 +134,7 @@ static int rule_cavalry_skill;
static int rule_population_damage;
static int rule_hero_speed;
static bool rule_anon_battle;
static bool rule_igjarjuk_curse;
static int rule_goblin_bonus;
static int rule_tactics_formula;
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_population_damage = config_get_int("rules.combat.populationdamage", 20);
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_skill = config_get_int("rules.cavalry.skill", 2);
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);
if (sh != NULL)
mod *= (1 + sh->penalty);
vw = (int)(100 - ((100 - vw) * mod));
vw = (int)(100.0 - ((100.0 - (double)vw) * mod));
do {
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) {
int damage = 0;
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;
kritchance = fmax(kritchance, 0.005);
@ -1748,13 +1750,14 @@ void do_combatmagic(battle * b, combatmagic_t was)
memset(spellranks, 0, sizeof(spellranks));
if (was == DO_PRECOMBATSPELL) {
if (rule_igjarjuk_curse && was == DO_PRECOMBATSPELL) {
summon_igjarjuk(b, spellranks);
}
for (s = b->sides; s != b->sides + b->nsides; ++s) {
fighter *fig;
for (fig = s->fighters; fig; fig = fig->next) {
unit *mage = fig->unit;
unit *caster = mage;
if (fig->alive <= 0)
continue; /* fighter kann im Kampf get<65>tet worden sein */
@ -1788,7 +1791,7 @@ void do_combatmagic(battle * b, combatmagic_t was)
continue;
}
level = eff_spelllevel(mage, sp, level, 1);
level = eff_spelllevel(mage, caster, sp, level, 1);
if (sl > 0 && sl < level) {
level = sl;
}
@ -1802,11 +1805,11 @@ void do_combatmagic(battle * b, combatmagic_t was)
free_order(ord);
if (power <= 0) { /* Effekt von Antimagie */
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)) {
report_failed_spell(b, mage, sp);
pay_spell(mage, sp, level, 1);
pay_spell(mage, NULL, sp, level, 1);
}
else {
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);
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);
free_castorder(&co);
if (level > 0) {
pay_spell(at.fighter->unit, sp, level, 1);
pay_spell(at.fighter->unit, NULL, sp, level, 1);
}
return level;
}
@ -1848,7 +1851,7 @@ static void do_combatspell(troop at)
{
const spell *sp;
fighter *fi = at.fighter;
unit *caster = fi->unit;
unit *mage = fi->unit;
battle *b = fi->side->battle;
region *r = b->region;
selist *ql;
@ -1857,28 +1860,28 @@ static void do_combatspell(troop at)
int fumblechance = 0;
order *ord;
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) {
fi->magic = 0; /* Hat keinen Kampfzauber, k<>mpft nichtmagisch weiter */
return;
}
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 */
return;
}
level = eff_spelllevel(caster, sp, fi->magic, 1);
sl = get_combatspelllevel(caster, 1);
level = eff_spelllevel(mage, mage, sp, fi->magic, 1);
sl = get_combatspelllevel(mage, 1);
if (sl > 0 && sl < level) {
level = sl;
}
if (fumble(r, caster, sp, level)) {
report_failed_spell(b, caster, sp);
pay_spell(caster, sp, level, 1);
if (fumble(r, mage, sp, level)) {
report_failed_spell(b, mage, sp);
pay_spell(mage, NULL, sp, level, 1);
return;
}
@ -1894,16 +1897,16 @@ static void do_combatspell(troop at)
/* Antimagie die Fehlschlag erh<72>ht */
if (rng_int() % 100 < fumblechance) {
report_failed_spell(b, caster, sp);
pay_spell(caster, sp, level, 1);
report_failed_spell(b, mage, sp);
pay_spell(mage, NULL, sp, level, 1);
free_order(ord);
return;
}
power = spellpower(r, caster, sp, level, ord);
power = spellpower(r, mage, sp, level, ord);
free_order(ord);
if (power <= 0) { /* Effekt von Antimagie */
report_failed_spell(b, caster, sp);
pay_spell(caster, sp, level, 1);
report_failed_spell(b, mage, sp);
pay_spell(mage, NULL, sp, level, 1);
return;
}
@ -2076,6 +2079,7 @@ void dazzle(battle * b, troop * td)
void damage_building(battle * b, building * bldg, int damage_abs)
{
assert(bldg);
bldg->size = MAX(1, bldg->size - damage_abs);
/* 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 *b = (battle *)calloc(1, sizeof(battle));
unit *u;
bfaction *bf;
building * bld;
battle *b = (battle *)calloc(1, sizeof(battle));
assert(b);
/* Alle Mann raus aus der Burg! */
for (bld = r->buildings; bld != NULL; bld = bld->next)
bld->sizeleft = bld->size;
@ -3447,6 +3452,7 @@ battle *make_battle(region * r)
}
if (!bf) {
bf = (bfaction *)calloc(1, sizeof(bfaction));
assert(bf);
++b->nfactions;
bf->faction = u->faction;
bf->next = b->factions;
@ -3470,14 +3476,15 @@ static void free_side(side * si)
static void free_fighter(fighter * fig)
{
armor **ap = &fig->armors;
while (*ap) {
armor *a = *ap;
*ap = a->next;
free(a);
}
while (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->weapons);
@ -3489,13 +3496,14 @@ static void battle_free(battle * b) {
assert(b);
for (s = b->sides; s != b->sides + b->nsides; ++s) {
fighter *fnext = s->fighters;
while (fnext) {
fighter *fig = fnext;
fnext = fig->next;
fighter **fp = &s->fighters;
while (*fp) {
fighter *fig = *fp;
*fp = fig->next;
free_fighter(fig);
free(fig);
}
s->fighters = NULL;
free_side(s);
}
free(b);

View File

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

View File

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

View File

@ -607,7 +607,7 @@ struct spellbook * faction_get_spellbook(struct faction *f)
if (f->magiegebiet != M_GRAY) {
return get_spellbook(magic_school[f->magiegebiet]);
}
return 0;
return NULL;
}
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 FACTION_UID_VERSION 362 /* f->uid contains a database id */
#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 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>
struct faction;
struct msglevel;
typedef struct mlist {
struct mlist *next;
@ -39,13 +38,6 @@ extern "C" {
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_ERROR 1
#define MESSAGE_MISSING_REPLACE 2

View File

@ -1293,50 +1293,63 @@ ship *read_ship(gamedata *data)
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;
for (r = regions; r; r = r->next) {
unit * u;
for (u = r->units; u; u = u->next) {
if (u->_race != u->faction->race && (u->_race->flags & RCF_FAMILIAR)) {
/* unit is potentially a familiar */
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));
}
}
}
}
callback(u);
}
}
}
@ -1524,7 +1537,10 @@ int read_game(gamedata *data)
}
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);

View File

@ -208,6 +208,7 @@ static buddy *get_friends(const unit * u, int *numfriends)
nf = *fr;
if (nf == NULL || nf->faction != u2->faction) {
nf = malloc(sizeof(buddy));
assert(nf);
nf->next = *fr;
nf->faction = u2->faction;
nf->unit = u2;
@ -1004,11 +1005,15 @@ skill *add_skill(unit * u, skill_t sk)
skill *sv;
int i;
assert(u);
for (i = 0; i != u->skill_size; ++i) {
sv = u->skills + i;
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;
if (i < u->skill_size) {
assert(sv->id != sk);
@ -1244,24 +1249,30 @@ int invisible(const unit * target, const unit * viewer)
*/
void free_unit(unit * u)
{
struct reservation **pres = &u->reservations;
assert(!u->region);
free(u->_name);
free_order(u->thisorder);
free_orders(&u->orders);
if (u->skills)
while (*pres) {
struct reservation *res = *pres;
*pres = res->next;
free(res);
}
if (u->skills) {
free(u->skills);
u->skills = NULL;
}
while (u->items) {
item *it = u->items->next;
u->items->next = NULL;
i_free(u->items);
u->items = it;
}
while (u->attribs)
while (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 0;
return NULL;
}
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.
* Um nur die Zahl der bereits gezauberten Sprüche zu ermitteln mit
* step = 0 aufrufen.
*/
int countspells(unit * u, int step)
{
@ -666,36 +664,60 @@ int countspells(unit * u, int step)
int count;
m = get_mage_depr(u);
if (!m)
if (!m) {
return 0;
if (step == 0)
}
if (step == 0) {
return m->spellcount;
}
count = m->spellcount + step;
m->spellcount = (count > 0) ? count : 0;
return m->spellcount;
}
/* ------------------------------------------------------------- */
/* Die für den Spruch benötigte Aura pro Stufe.
* Die Grundkosten pro Stufe werden hier um 2^count erhöht. Der
* Parameter count ist dabei die Anzahl der bereits gezauberten Sprüche
int spellcount(const unit *u) {
sc_mage *m = get_mage_depr(u);
if (m) {
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);
for (k = 0; sp->components && sp->components[k].type; k++) {
if (sp->components[k].type == r_aura) {
aura = sp->components[k].amount;
if (spc->type == r_aura) {
return spc->amount * aura_multiplier(caster);
}
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 aura;
return 0;
}
/* ------------------------------------------------------------- */
@ -726,17 +748,17 @@ static int spl_costtyp(const spell * sp)
return costtyp;
}
/* ------------------------------------------------------------- */
/* durch Komponenten und cast_level begrenzter maximal möglicher
* Level
/**
* Durch Komponenten und cast_level begrenzter maximal möglicherLevel.
*
* Da die Funktion nicht alle Komponenten durchprobiert sondern beim
* ersten Fehler abbricht, muss die Fehlermeldung später mit cancast()
* 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);
int k, maxlevel, needplevel;
int k, maxlevel;
int costtyp = SPC_FIX;
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;
if (sp->components[k].amount > 0) {
/* Die Kosten für Aura sind auch von der Zahl der bereits
* gezauberten Sprüche abhängig */
int level_cost = sp->components[k].amount * range;
if (sp->components[k].type == r_aura) {
needplevel = spellcost(u, sp) * range;
}
else {
needplevel = sp->components[k].amount * range;
/* 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,
needplevel * cast_level) / needplevel;
level_cost * cast_level) / level_cost;
/* sind die Kosten fix, so muss die Komponente nur einmal vorhanden
* 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
* 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 resuse;
assert(cast_level > 0);
for (k = 0; sp->components && sp->components[k].type; k++) {
if (sp->components[k].type == r_aura) {
resuse = spellcost(u, sp) * range;
}
else {
resuse = sp->components[k].amount * range;
}
const struct spell_component *spc = sp->components + k;
int resuse = spellcost(caster ? caster : mage, spc) * range;
if (sp->components[k].cost == SPC_LINEAR
|| sp->components[k].cost == SPC_LEVEL) {
if (spc->cost == SPC_LINEAR || spc->cost == SPC_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
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 itemanz;
resource *reslist = NULL;
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) {
if (sp->components[k].amount > 0) {
const resource_type *rtype = sp->components[k].type;
int itemhave;
const struct spell_component *spc = sp->components + k;
if (spc->amount > 0) {
const resource_type *rtype = spc->type;
int itemhave, itemanz;
/* Die Kosten für Aura sind auch von der Zahl der bereits
* gezauberten Sprüche abhängig */
if (rtype == r_aura) {
itemanz = spellcost(u, sp) * range;
}
else {
itemanz = sp->components[k].amount * range;
}
itemanz = spellcost(u, spc) * range;
/* sind die Kosten stufenabhängig, so muss itemanz noch mit dem
* level multipliziert werden */
switch (sp->components[k].cost) {
switch (spc->cost) {
case SPC_LEVEL:
case SPC_LINEAR:
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);
if (itemhave < itemanz) {
resource *res = malloc(sizeof(resource));
assert(res);
res->number = itemanz - itemhave;
res->type = rtype;
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 */
mage = get_mage_depr(u);
if (mage->magietyp == M_DRAIG) {
mage = get_mage(u);
if (mage && mage->magietyp == M_DRAIG) {
fumble_chance += CHAOSPATZERCHANCE;
}
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 */
static void fumble_default(castorder * co)
{
unit *mage = co->magician.u;
unit *mage = co_get_magician(co);
cmistake(mage, co->order, 180, MSG_MAGIC);
@ -1295,7 +1303,8 @@ static void do_fumble(castorder * co)
{
curse *c;
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;
int level = co->level;
int duration;
@ -1304,8 +1313,8 @@ static void do_fumble(castorder * co)
static int rc_cache;
fumble_f fun;
ADDMSG(&u->faction->msgs,
msg_message("patzer", "unit region spell", u, r, sp));
ADDMSG(&mage->faction->msgs,
msg_message("patzer", "unit region spell", mage, r, sp));
switch (rng_int() % 10) {
case 0:
/* 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
* 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)) {
const resource_type *rtype = rt_find("toadslime");
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;
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)) {
rc_toad = get_race(RC_TOAD);
}
u_setrace(u, rc_toad);
u->irace = NULL;
ADDMSG(&r->msgs, msg_message("patzer6", "unit region spell", u, r, sp));
u_setrace(mage, rc_toad);
mage->irace = NULL;
ADDMSG(&r->msgs, msg_message("patzer6", "unit region spell", mage, r, sp));
break;
}
/* fall-through is intentional! */
@ -1350,26 +1359,26 @@ static void do_fumble(castorder * co)
duration = rng_int() % level / 2;
if (duration < 2) duration = 2;
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);
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;
case 3:
case 4:
/* Spruch schlägt fehl, alle Magiepunkte weg */
set_spellpoints(u, 0);
ADDMSG(&u->faction->msgs, msg_message("patzer3", "unit region spell",
u, r, sp));
set_spellpoints(mage, 0);
ADDMSG(&mage->faction->msgs, msg_message("patzer3", "unit region spell",
mage, r, sp));
break;
case 5:
case 6:
/* Spruch gelingt, aber alle Magiepunkte weg */
co->level = cast_spell(co);
set_spellpoints(u, 0);
ADDMSG(&u->faction->msgs, msg_message("patzer4", "unit region spell",
u, r, sp));
set_spellpoints(mage, 0);
ADDMSG(&mage->faction->msgs, msg_message("patzer4", "unit region spell",
mage, r, sp));
break;
case 7:
@ -1378,9 +1387,9 @@ static void do_fumble(castorder * co)
default:
/* Spruch gelingt, alle nachfolgenden Sprüche werden 2^4 so teuer */
co->level = cast_spell(co);
ADDMSG(&u->faction->msgs, msg_message("patzer5", "unit region spell",
u, r, sp));
countspells(u, 3);
ADDMSG(&mage->faction->msgs, msg_message("patzer5", "unit region spell",
mage, r, sp));
countspells(caster, 3);
}
}
@ -1591,7 +1600,7 @@ verify_unit(region * r, unit * mage, const spell * sp, spllprm * spobj,
static void
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;
region *target_r = co_get_region(co);
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;
}
struct unit * co_get_magician(const struct castorder * co) {
return co->magician.u;
}
struct region * co_get_region(const struct castorder * co) {
return co->_rtarget;
}
@ -2432,7 +2445,7 @@ static castorder *cast_cmd(unit * u, order * ord)
spell *sp = 0;
plane *pl;
spellparameter *args = NULL;
unit * caster = u;
unit * mage = u;
param_t param;
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
* die Einheit beherrscht. */
if (!sp && is_familiar(u)) {
caster = get_familiar_mage(u);
if (caster) {
mage = get_familiar_mage(u);
if (mage) {
familiar = u;
sp = unit_getspell(caster, s, caster->faction->locale);
sp = unit_getspell(mage, s, mage->faction->locale);
}
else {
/* somehow, this familiar has no mage! */
log_error("cast_cmd: familiar %s is without a mage?\n", unitname(u));
caster = u;
mage = u;
}
}
@ -2597,24 +2610,22 @@ static castorder *cast_cmd(unit * u, order * ord)
cmistake(u, ord, 177, MSG_MAGIC);
return 0;
}
if (caster != familiar) { /* Magier zaubert durch Vertrauten */
if (mage != familiar) { /* Magier zaubert durch Vertrauten */
int sk;
if (range > 1) { /* Fehler! Versucht zu Farcasten */
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "familiar_farcast",
"mage", caster));
"mage", mage));
return 0;
}
sk = effskill(caster, SK_MAGIC, 0);
if (distance(caster->region, r) > sk) {
sk = effskill(mage, SK_MAGIC, 0);
if (distance(mage->region, r) > sk) {
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "familiar_toofar",
"mage", caster));
"mage", mage));
return 0;
}
/* mage auf magier setzen, level anpassen, range für Erhöhung
* der Spruchkosten nutzen, langen Befehl des Magiers
* löschen, zaubern kann er noch */
* der Spruchkosten nutzen */
range *= 2;
set_order(&caster->thisorder, NULL);
sk /= 2;
if (level > sk) level = sk;
}
@ -2635,7 +2646,7 @@ static castorder *cast_cmd(unit * u, order * ord)
unitname(u), MAX_PARAMETERS, sp->sname);
}
args =
add_spellparameter(target_r, caster, sp->parameter,
add_spellparameter(target_r, mage, sp->parameter,
(const char *const *)params, p, ord);
for (i = 0; i != p; ++i) {
free(params[i]);
@ -2645,7 +2656,7 @@ static castorder *cast_cmd(unit * u, order * ord)
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);
}
@ -2718,16 +2729,17 @@ void magic(void)
order *ord = co->order;
int invalid, resist, success, cast_level = co->level;
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;
region *target_r = co_get_region(co);
/* 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) {
/* Fehlermeldung mit Komponenten generieren */
cancast(u, sp, cast_level, co->distance, ord);
cancast(mage, sp, cast_level, co->distance, ord);
continue;
}
@ -2735,24 +2747,24 @@ void magic(void)
/* Sprüche mit Fixkosten werden immer auf Stufe des Spruchs
* gezaubert, co->level ist aber defaultmäßig Stufe des Magiers */
if (spl_costtyp(sp) != SPC_FIX) {
ADDMSG(&u->faction->msgs, msg_message("missing_components",
"unit spell level", u, sp, cast_level));
ADDMSG(&mage->faction->msgs, msg_message("missing_components",
"unit spell level", mage, sp, cast_level));
}
}
/* Prüfen, ob die realen Kosten für die gewünschten Stufe bezahlt
* 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 */
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 */
if (co->force <= 0) {
co->force = 0;
ADDMSG(&u->faction->msgs, msg_message("missing_force",
"unit spell level", u, sp, co->level));
ADDMSG(&mage->faction->msgs, msg_message("missing_force",
"unit spell level", mage, sp, co->level));
}
/* Ziele auf Existenz prüfen und Magieresistenz feststellen. Wurde
@ -2772,15 +2784,15 @@ void magic(void)
co->force = 0;
/* zwar wurde mindestens ein Ziel gefunden, das widerstand
* jedoch dem Zauber. Kosten abziehen und abbrechen. */
ADDMSG(&u->faction->msgs, msg_message("spell_resist",
"unit region spell", u, r, sp));
ADDMSG(&mage->faction->msgs, msg_message("spell_resist",
"unit region spell", mage, r, sp));
}
}
/* Auch für Patzer gibt es Erfahrung, müssen die Spruchkosten
* bezahlt werden und die nachfolgenden Sprüche werden teurer */
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 */
fumbled = true;
}
@ -2794,12 +2806,12 @@ void magic(void)
}
/* erst bezahlen, dann Kostenzähler erhöhen */
if (co->level > 0) {
pay_spell(u, sp, co->level, co->distance);
pay_spell(mage, caster, sp, co->level, co->distance);
}
if (fumbled) {
do_fumble(co);
}
countspells(u, 1);
countspells(caster, 1);
}
}
@ -2807,9 +2819,9 @@ void magic(void)
for (r = regions; r; r = r->next) {
unit *u;
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);
/* 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)
{
void * tokens = 0;
spellbook *sb;
sb = unit_get_spellbook(u);
if (sb) {
void * tokens = NULL;
select_spellbook(&tokens, sb, lang);
}
#if 0 /* TODO: some familiars can cast spells from the mage's spellbook? */
u = get_familiar_mage(u);
if (u) {
sb = unit_get_spellbook(u);
if (sb) {
select_spellbook(&tokens, sb, lang);
}
}
#endif
if (tokens) {
variant token;
if (findtoken(tokens, name, &token) != E_TOK_NOMATCH) {
if (tokens) {
variant token;
if (findtoken(tokens, name, &token) != E_TOK_NOMATCH) {
freetokens(tokens);
return (spell *)token.v;
}
freetokens(tokens);
return (spell *)token.v;
}
freetokens(tokens);
}
return 0;
return NULL;
}
int cast_spell(struct castorder *co)

View File

@ -140,6 +140,7 @@ extern "C" {
} castorder;
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);
typedef struct spell_component {
@ -288,9 +289,11 @@ extern "C" {
/* Prüfroutinen für Zaubern */
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
* 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
* geringstmöglichen Stufe zurück, schon um den Faktor der bereits
* zuvor gezauberten Sprüche erhöht */
@ -298,12 +301,12 @@ extern "C" {
int distance, struct order *ord);
/* true, wenn Einheit alle Komponenten des Zaubers (incl. MP) für die
* 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
* ab. Die effektive Stufe des gezauberten Spruchs ist wichtig für
* die korrekte Bestimmung der Magiepunktkosten */
int eff_spelllevel(struct unit *u, const struct spell * sp, int cast_level,
int distance);
int eff_spelllevel(struct unit *mage, struct unit *caster,
const struct spell * sp, int cast_level, int distance);
/* ermittelt die effektive Stufe des Zaubers. Dabei ist cast_level
* die gewünschte maximale Stufe (im Normalfall Stufe des Magiers,
* 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_HORSE), 3);
level = eff_spelllevel(u, sp, 3, 1);
level = eff_spelllevel(u, u, sp, 3, 1);
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_AURA)));
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, 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);
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, 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, 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, eff_spelllevel(u, sp, 2, 1));
CuAssertIntEquals(tc, 0, eff_spelllevel(u, u, sp, 2, 1));
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;
unit *u;
region *r;
unit *mage;
unit *caster;
int cast_level;
double power;
spellparameter *pa;
@ -47,7 +47,7 @@ int sp_flying_ship(castorder * co)
assert(co);
r = co_get_region(co);
mage = co->magician.u;
caster = co_get_caster(co);
cast_level = co->level;
power = co->force;
pa = co->par;
@ -57,22 +57,22 @@ int sp_flying_ship(castorder * co)
return 0;
sh = pa->param[0]->data.sh;
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));
return 0;
}
/* Duration = 1, nur diese Runde */
cno = levitate_ship(sh, mage, power, 1);
cno = levitate_ship(sh, caster, power, 1);
if (cno == 0) {
if (is_cursed(sh->attribs, &ct_flyingship)) {
/* 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)) {
/* 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;
}
@ -86,7 +86,7 @@ int sp_flying_ship(castorder * co)
if (!(u->faction->flags & FFL_SELECT)) {
u->faction->flags |= FFL_SELECT;
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);
}

View File

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

View File

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