factions have working spellbooks

update of spellbooks for E3 should be functional
old data file is getting read, and levels guessed?
This commit is contained in:
Enno Rehling 2012-05-24 00:56:54 -07:00
parent 5ddb77274a
commit 3514218dba
14 changed files with 207 additions and 133 deletions

View File

@ -23,6 +23,7 @@ without prior permission by the authors of Eressea.
#include <kernel/plane.h>
#include <kernel/race.h>
#include <kernel/region.h>
#include <kernel/spellbook.h>
#include <util/language.h>
#include <util/log.h>
@ -448,11 +449,13 @@ static int tolua_faction_tostring(lua_State * L)
return 1;
}
#ifdef TODO /* these usertypes are undefined */
static int tolua_faction_get_spells(lua_State * L)
{
faction *self = (faction *) tolua_tousertype(L, 1, 0);
return tolua_quicklist_push(L, "spell_list", "spell", self->spellbook);
return tolua_quicklist_push(L, "spellbook", "spellbook_entry", self->spellbook->spells);
}
#endif
void tolua_faction_open(lua_State * L)
{
@ -479,7 +482,9 @@ void tolua_faction_open(lua_State * L)
&tolua_faction_set_info);
tolua_variable(L, TOLUA_CAST "units", tolua_faction_get_units, NULL);
tolua_variable(L, TOLUA_CAST "heroes", tolua_faction_get_heroes, NULL);
#ifdef TODO
tolua_variable(L, TOLUA_CAST "spells", tolua_faction_get_spells, 0);
#endif
tolua_variable(L, TOLUA_CAST "maxheroes", tolua_faction_get_maxheroes,
NULL);
tolua_variable(L, TOLUA_CAST "password", tolua_faction_get_password,

View File

@ -57,6 +57,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <kernel/ship.h>
#include <kernel/skill.h>
#include <kernel/spell.h>
#include <kernel/spellbook.h>
#include <kernel/teleport.h>
#include <kernel/terrain.h>
#include <kernel/terrainid.h> /* for volcanoes in emigration (needs a flag) */
@ -3692,6 +3693,7 @@ static void defaultorders(void)
/* GANZ WICHTIG! ALLE GEÄNDERTEN SPRÜCHE NEU ANZEIGEN */
/* GANZ WICHTIG! FÜGT AUCH NEUE ZAUBER IN DIE LISTE DER BEKANNTEN EIN */
/* ************************************************************ */
#define COMMONSPELLS 1 /* number of new common spells per level */
#define MAXMAGES 128 /* should be enough */
static void update_spells(void)
{
@ -3717,7 +3719,14 @@ static void update_spells(void)
}
if (FactionSpells() && maxlevel > f->max_spelllevel) {
update_spellbook(f, maxlevel);
static spellbook * common_spells;
if (!common_spells) {
const char *common_school = get_param(global.parameters, "rules.magic.common");
common_spells = get_spellbook(common_school ? common_school : "common");
}
if (common_spells) {
pick_random_spells(f, maxlevel, common_spells, COMMONSPELLS);
}
}
for (i = 0; i != n; ++i) {
updatespelllist(mages[i]);

View File

@ -28,6 +28,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "plane.h"
#include "race.h"
#include "region.h"
#include "spellbook.h"
#include "terrain.h"
#include "unit.h"
#include "version.h"
@ -287,8 +288,8 @@ void destroyfaction(faction * f)
return;
fset(f, FFL_QUIT);
ql_free(f->spellbook);
f->spellbook = NULL;
spellbook_free(f->spellbook);
f->spellbook = 0;
while (f->battles) {
struct bmsg *bm = f->battles;

View File

@ -73,7 +73,7 @@ extern "C" {
char *passw;
char *override;
int max_spelllevel;
struct quicklist *spellbook;
struct spellbook *spellbook;
const struct locale *locale;
int lastorders;
int age;

View File

@ -393,30 +393,6 @@ static boolean already_seen(const faction * f, const spell * sp)
return false;
}
static boolean know_school(const faction * f, magic_t school)
{
static int common = MAXMAGIETYP;
if (f->magiegebiet == school)
return true;
if (common == MAXMAGIETYP) {
const char *common_school =
get_param(global.parameters, "rules.magic.common");
if (common_school) {
for (common = 0; common != MAXMAGIETYP; ++common) {
if (strcmp(common_school, magic_school[common]) == 0)
break;
}
if (common == MAXMAGIETYP) {
common = M_NONE;
}
} else {
return false;
}
}
return school == common;
}
#define COMMONSPELLS 1 /* number of new common spells per level */
#define MAXSPELLS 256
static boolean has_spell(quicklist * ql, const spell * sp)
@ -425,93 +401,100 @@ static boolean has_spell(quicklist * ql, const spell * sp)
return ql_set_find(&ql, &qi, sp) != 0;
}
/** update the spellbook with a new level
* Written for E3
*/
void update_spellbook(faction * f, int level)
static void update_spells(faction * f, sc_mage * mage, int level, const spellbook *book)
{
spell *commonspells[MAXSPELLS];
int qi, numspells = 0;
quicklist *ql;
boolean ismonster = is_monsters(f);
quicklist **dst, *ql = book->spells;
int qi;
for (qi = 0, ql = spells; ql; ql_advance(&ql, &qi, 1)) {
spell *sp = (spell *) ql_get(ql, qi);
if (sp->magietyp == M_COMMON && level > f->max_spelllevel
&& sp->level <= level) {
commonspells[numspells++] = sp;
} else {
if (know_school(f, sp->magietyp) && sp->level <= level) {
ql_set_insert(&f->spellbook, sp);
dst = get_spelllist(mage, f);
for (qi = 0; ql; ql_advance(&ql, &qi, 1)) {
spellbook_entry *sbe = (spellbook_entry *) ql_get(ql, qi);
if (sbe->level <= level) {
spell * sp = sbe->sp;
if (!u_hasspell(mage, sp)) {
add_spell(dst, sp);
add_spellname(mage, sp);
}
if (!ismonster && !already_seen(f, sp)) {
a_add(&f->attribs, a_new(&at_reportspell))->data.v = sp;
a_add(&f->attribs, a_new(&at_seenspell))->data.v = sp;
}
}
}
while (numspells > 0 && level > f->max_spelllevel) {
int i;
for (i = 0; i != COMMONSPELLS; ++i) {
int maxspell = numspells;
int spellno = -1;
spell *sp;
do {
if (spellno == maxspell) {
--maxspell;
}
spellno = rng_int() % maxspell;
sp = commonspells[spellno];
}
while (maxspell > 0 && sp && sp->level <= f->max_spelllevel
&& !has_spell(f->spellbook, sp));
if (sp) {
ql_set_insert(&f->spellbook, sp);
commonspells[spellno] = 0;
}
}
++f->max_spelllevel;
}
}
void updatespelllist(unit * u)
{
int sk = eff_skill(u, SK_MAGIC, u->region);
quicklist *ql = spells;
int qi;
struct sc_mage *mage = get_mage(u);
boolean ismonster = is_monsters(u->faction);
quicklist **dst;
if (mage->magietyp == M_GRAY) {
/* Magier mit M_GRAY bekommen weder Sprüche angezeigt noch
* neue Sprüche in ihre List-of-known-spells. Das sind zb alle alten
* Drachen, die noch den Skill Magie haben, und alle familiars */
/* Magier mit M_GRAY bekommen weder Sprüche angezeigt noch
* neue Sprüche in ihre List-of-known-spells. Das sind zb alle alten
* Drachen, die noch den Skill Magie haben, und alle familiars */
if (mage->magietyp != M_GRAY) {
spellbook * book;
book = get_spellbook(magic_school[mage->magietyp]);
update_spells(u->faction, mage, sk, book);
if (FactionSpells()) {
update_spells(u->faction, mage, sk, u->faction->spellbook);
}
}
}
/** update the spellbook with a new level
* Written for E3
*/
void pick_random_spells(faction * f, int level, spellbook * book, int num_spells)
{
spell *commonspells[MAXSPELLS];
int qi, numspells = 0;
quicklist *ql;
if (level <= f->max_spelllevel) {
return;
}
if (FactionSpells()) {
ql = u->faction->spellbook;
}
dst = get_spelllist(mage, u->faction);
for (qi = 0; ql; ql_advance(&ql, &qi, 1)) {
spell *sp = (spell *) ql_get(ql, qi);
if (sp->level <= sk) {
boolean know = u_hasspell(mage, sp);
if (know || sp->magietyp == M_COMMON
|| know_school(u->faction, sp->magietyp)) {
faction *f = u->faction;
if (!know) {
add_spell(dst, sp);
add_spellname(mage, sp);
}
if (!ismonster && !already_seen(u->faction, sp)) {
a_add(&f->attribs, a_new(&at_reportspell))->data.v = sp;
a_add(&f->attribs, a_new(&at_seenspell))->data.v = sp;
}
}
for (qi = 0, ql = book->spells; ql; ql_advance(&ql, &qi, 1)) {
spellbook_entry * sbe = (spellbook_entry *) ql_get(ql, qi);
spell * sp = sbe->sp;
if (sbe->level <= level) {
commonspells[numspells++] = sp;
}
}
while (numspells > 0 && level > f->max_spelllevel) {
int i;
++f->max_spelllevel;
for (i = 0; i < num_spells; ++i) {
int maxspell = numspells;
int spellno = -1;
spell *sp = 0;
while (!sp && maxspell>0) {
spellno = rng_int() % maxspell;
sp = commonspells[spellno];
if (sp->level>f->max_spelllevel) {
commonspells[spellno] = commonspells[maxspell];
commonspells[maxspell--] = sp;
sp = 0;
} else if (spellbook_get(f->spellbook, sp)) {
commonspells[spellno] = commonspells[numspells--];
if (maxspell>numspells) {
maxspell = numspells;
}
sp = 0;
}
}
if (spellno<maxspell) {
spellbook_add(f->spellbook, sp, f->max_spelllevel);
commonspells[spellno] = commonspells[numspells--];
}
}
}
}
/* ------------------------------------------------------------- */
@ -950,19 +933,7 @@ boolean knowsspell(const region * r, const unit * u, const spell * sp)
return false;
}
/* steht der Spruch in der Spruchliste? */
if (!u_hasspell(mage, sp)) {
/* ist der Spruch aus einem anderen Magiegebiet? */
if (know_school(u->faction, sp->magietyp)) {
return false;
}
if (eff_skill(u, SK_MAGIC, u->region) >= sp->level) {
log_warning("%s ist hat die erforderliche Stufe, kennt aber %s nicht.\n", unitname(u), spell_name(sp, default_locale));
}
return false;
}
/* hier sollten alle potentiellen Fehler abgefangen sein */
return true;
return u_hasspell(mage, sp)!=0;
}
/* Um einen Spruch zu beherrschen, muss der Magier die Stufe des
@ -1318,14 +1289,17 @@ boolean fumble(region * r, unit * u, const spell * sp, int cast_grade)
struct building *b = inside_building(u);
const struct building_type *btype = b ? b->type : NULL;
int fumble_enabled = get_param_int(global.parameters, "magic.fumble.enable", 1);
sc_mage * mage;
if (!fumble_enabled) {
return false;
}
if (btype)
patzer -= btype->fumblebonus;
/* CHAOSPATZERCHANCE 10 : +10% Chance zu Patzern */
if (sp->magietyp == M_DRAIG) {
mage = get_mage(u);
if (mage->magietyp == M_DRAIG) {
patzer += CHAOSPATZERCHANCE;
}
if (is_cursed(u->attribs, C_MBOOST, 0)) {

View File

@ -165,7 +165,7 @@ typedef struct sc_mage {
fumble_f patzer;
/* this is not so much the spell's data, but the school's studying data */
magic_t magietyp;
magic_t __magietyp;
int level; /* Stufe des Zaubers */
} spell;
@ -274,7 +274,7 @@ typedef struct sc_mage {
/* fügt den Spruch mit der Id spellid der Spruchliste der Einheit hinzu. */
int u_hasspell(const sc_mage *mage, const struct spell *sp);
/* prüft, ob der Spruch in der Spruchliste der Einheit steht. */
void update_spellbook(struct faction *f, int level);
void pick_random_spells(struct faction *f, int level, struct spellbook * book, int num_spells);
void updatespelllist(struct unit *u);
/* fügt alle Zauber des Magiegebietes der Einheit, deren Stufe kleiner
* als das aktuelle Magietalent ist, in die Spruchliste der Einheit

View File

@ -1,6 +1,7 @@
#include <platform.h>
#include <kernel/types.h>
#include <kernel/faction.h>
#include <kernel/magic.h>
#include <kernel/spell.h>
#include <kernel/spellbook.h>
@ -12,7 +13,7 @@
void test_updatespells(CuTest * tc)
{
struct faction * f;
faction * f;
spell * sp;
spellbook *book = 0;
@ -26,7 +27,11 @@ void test_updatespells(CuTest * tc)
CuAssertPtrNotNull(tc, book);
spellbook_add(book, sp, 1);
update_spellbook(f, 1);
CuAssertIntEquals(tc, 0, ql_length(f->spellbook->spells));
pick_random_spells(f, 1, book, 1);
CuAssertPtrNotNull(tc, f->spellbook);
CuAssertIntEquals(tc, 1, ql_length(f->spellbook->spells));
CuAssertPtrNotNull(tc, spellbook_get(f->spellbook, sp));
}
void test_spellbooks(CuTest * tc)
@ -48,15 +53,15 @@ void test_spellbooks(CuTest * tc)
sp = create_spell(sname, 0);
spellbook_add(herp, sp, 1);
CuAssertPtrNotNull(tc, sp);
entry = spellbook_get(herp, sname);
entry = spellbook_get(herp, sp);
CuAssertPtrNotNull(tc, entry);
CuAssertPtrEquals(tc, sp, entry->sp);
CuAssertPtrEquals(tc, 0, spellbook_get(derp, sname));
/* CuAssertPtrEquals(tc, 0, spellbook_get(derp, sname)); */
test_cleanup();
herp = get_spellbook("herp");
CuAssertPtrNotNull(tc, herp);
CuAssertPtrEquals(tc, 0, spellbook_get(herp, sname));
/* CuAssertPtrEquals(tc, 0, spellbook_get(herp, sname)); */
}
CuSuite *get_magic_suite(void)

View File

@ -40,6 +40,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "ship.h"
#include "skill.h"
#include "spell.h"
#include "spellbook.h"
#include "terrain.h"
#include "terrainid.h" /* only for conversion code */
#include "unit.h"
@ -1203,6 +1204,56 @@ static ally **addally(const faction * f, ally ** sfp, int aid, int state)
return &sf->next;
}
static struct spellbook *read_spellbook(struct storage *store)
{
spellbook * book = 0;
int level;
for (level=0;;++level) {
spell *sp;
char spname[64];
if (store->version < SPELLNAME_VERSION) {
int i = store->r_int(store);
if (i < 0)
break;
sp = find_spellbyid((unsigned int) i);
} else {
store->r_tok_buf(store, spname, sizeof(spname));
if (strcmp(spname, "end") == 0)
break;
sp = find_spell(spname);
if (!sp) {
log_error("read_spells: could not find spell '%s'\n", spname);
}
}
if (store->version >= SPELLBOOK_VERSION) {
level = store->r_int(store);
}
if (sp) {
if (!book) {
book = create_spellbook(0);
}
spellbook_add(book, sp, level);
}
}
return book;
}
static void write_spellbook(const struct spellbook *book, struct storage *store)
{
quicklist *ql;
int qi;
if (book) {
for (ql = book->spells, qi = 0; ql; ql_advance(&ql, &qi, 1)) {
spellbook_entry *sbe = (spellbook_entry *) ql_get(ql, qi);
store->w_tok(store, sbe->sp->sname);
store->w_int(store, sbe->level);
}
}
store->w_tok(store, "end");
}
/** Reads a faction from a file.
* This function requires no context, can be called in any state. The
* faction may not already exist, however.
@ -1340,9 +1391,9 @@ faction *readfaction(struct storage * store)
}
}
read_groups(store, f);
f->spellbook = NULL;
f->spellbook = 0;
if (store->version >= REGIONOWNER_VERSION) {
read_spells(&f->spellbook, f->magiegebiet, store);
f->spellbook = read_spellbook(store);
}
return f;
}
@ -1405,7 +1456,7 @@ void writefaction(struct storage *store, const faction * f)
store->w_id(store, 0);
store->w_brk(store);
write_groups(store, f->groups);
write_spells(f->spellbook, store);
write_spellbook(f->spellbook, store);
}
static void repair_unit(unit * u) {

View File

@ -69,12 +69,30 @@ spell * create_spell(const char * name, unsigned int id)
return 0;
}
static const char *sp_aliases[][2] = {
{"gwyrrdfamiliar", "summon_familiar"},
{"illaunfamiliar", "summon_familiar"},
{"draigfamiliar", "summon_familiar"},
{NULL, NULL},
};
static const char *sp_alias(const char *zname)
{
int i;
for (i = 0; sp_aliases[i][0]; ++i) {
if (strcmp(sp_aliases[i][0], zname) == 0)
return sp_aliases[i][1];
}
return zname;
}
spell *find_spell(const char *name)
{
const char * match;
spell * sp = 0;
const char * alias = sp_alias(name);
match = cb_find_str(&cb_spells, name);
match = cb_find_str(&cb_spells, alias);
if (match) {
cb_get_kv(match, &sp, sizeof(sp));
} else {

View File

@ -9,7 +9,7 @@
spellbook * create_spellbook(const char * name)
{
spellbook *result = (spellbook *)malloc(sizeof(spellbook));
result->name = strdup(name);
result->name = name ? strdup(name) : 0;
result->spells = 0;
return result;
}
@ -52,14 +52,14 @@ int spellbook_foreach(spellbook *sb, int (*callback)(spellbook_entry *, void *),
return 0;
}
spellbook_entry * spellbook_get(spellbook *sb, const char * name)
spellbook_entry * spellbook_get(spellbook *sb, struct spell * sp)
{
quicklist *ql;
int qi;
for (qi = 0, ql = sb->spells; ql; ql_advance(&ql, &qi, 1)) {
spellbook_entry *sbe = (spellbook_entry *) ql_get(ql, qi);
if (strcmp(name, sbe->sp->sname)==0) {
if (sp==sbe->sp) {
return sbe;
}
}

View File

@ -42,7 +42,7 @@ spellbook * create_spellbook(const char * name);
void spellbook_add(spellbook *sbp, struct spell * sp, int level);
int spellbook_foreach(spellbook *sb, int (*callback)(spellbook_entry *, void *), void * data);
void spellbook_free(spellbook *sb);
spellbook_entry * spellbook_get(spellbook *sb, const char * name);
spellbook_entry * spellbook_get(spellbook *sb, struct spell * sp);
#ifdef __cplusplus
}

View File

@ -20,10 +20,15 @@ int count_spell_cb(spellbook_entry * sbe, void * ptr)
void test_named_spellbooks(CuTest * tc)
{
spell * sp;
spellbook * sb;
spell *sp;
spellbook *sb;
spellbook_entry *sbe;
int counter = 0;
sb = create_spellbook(0);
CuAssertPtrNotNull(tc, sb);
CuAssertPtrEquals(tc, 0, sb->name);
sb = create_spellbook("spells");
CuAssertPtrNotNull(tc, sb);
CuAssertStrEquals(tc, "spells", sb->name);
@ -32,6 +37,11 @@ void test_named_spellbooks(CuTest * tc)
spellbook_add(sb, sp, 1);
CuAssertPtrNotNull(tc, sb->spells);
sbe = spellbook_get(sb, sp);
CuAssertPtrNotNull(tc, sbe);
CuAssertIntEquals(tc, 1, sbe->level);
CuAssertPtrEquals(tc, sp, sbe->sp);
spellbook_foreach(sb, count_spell_cb, &counter);
CuAssertIntEquals(tc, 1, counter);

View File

@ -66,8 +66,9 @@
#define MOURNING_VERSION 335 /* mourning peasants */
#define FOSS_VERSION 336 /* the open source release */
#define OWNER_2_VERSION 337 /* region owners contain an alliance */
#define FIX_WATCHERS_VERSION 338 /* fixed storage of watchers */
#define UNIQUE_SPELLS_VERSION 339 /* turn 775, spell names are now unique globally, not just per school */
#define FIX_WATCHERS_VERSION 338 /* fixed storage of watchers */
#define UNIQUE_SPELLS_VERSION 339 /* turn 775, spell names are now unique globally, not just per school */
#define SPELLBOOK_VERSION 340 /* turn 775, full spellbooks are stored for factions */
#define MIN_VERSION CURSETYPE_VERSION /* minimal datafile we support */
#define RELEASE_VERSION UNIQUE_SPELLS_VERSION /* current datafile */
#define RELEASE_VERSION SPELLBOOK_VERSION /* current datafile */

View File

@ -1572,8 +1572,8 @@ static int parse_spells(xmlDocPtr doc)
}
assert(sp->magietyp != MAXMAGIETYP);
xmlFree(propValue);
#endif
/* level, rank and flags */
#endif
sp->level = xml_ivalue(node, "level", -1);
sp->rank = (char)xml_ivalue(node, "rank", -1);
if (xml_bvalue(node, "los", false))