diff --git a/src/bindings/bind_faction.c b/src/bindings/bind_faction.c index 40aa6b113..97abfa521 100644 --- a/src/bindings/bind_faction.c +++ b/src/bindings/bind_faction.c @@ -23,6 +23,7 @@ without prior permission by the authors of Eressea. #include #include #include +#include #include #include @@ -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, diff --git a/src/gamecode/laws.c b/src/gamecode/laws.c index 7014f981e..9fdfd964a 100644 --- a/src/gamecode/laws.c +++ b/src/gamecode/laws.c @@ -57,6 +57,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include #include #include /* 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]); diff --git a/src/kernel/faction.c b/src/kernel/faction.c index 92cd0ac6a..f477b0858 100644 --- a/src/kernel/faction.c +++ b/src/kernel/faction.c @@ -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; diff --git a/src/kernel/faction.h b/src/kernel/faction.h index 377f4f305..ec82bbe51 100644 --- a/src/kernel/faction.h +++ b/src/kernel/faction.h @@ -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; diff --git a/src/kernel/magic.c b/src/kernel/magic.c index 78eabe9b3..8d80846d5 100644 --- a/src/kernel/magic.c +++ b/src/kernel/magic.c @@ -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 (spellnospellbook, 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)) { diff --git a/src/kernel/magic.h b/src/kernel/magic.h index b7c9d8a2b..b129368e9 100644 --- a/src/kernel/magic.h +++ b/src/kernel/magic.h @@ -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 diff --git a/src/kernel/magic_test.c b/src/kernel/magic_test.c index 7f8994d18..49960933d 100644 --- a/src/kernel/magic_test.c +++ b/src/kernel/magic_test.c @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -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) diff --git a/src/kernel/save.c b/src/kernel/save.c index 65737396a..507592ab6 100644 --- a/src/kernel/save.c +++ b/src/kernel/save.c @@ -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) { diff --git a/src/kernel/spell.c b/src/kernel/spell.c index 654a2d19d..e674796a5 100644 --- a/src/kernel/spell.c +++ b/src/kernel/spell.c @@ -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 { diff --git a/src/kernel/spellbook.c b/src/kernel/spellbook.c index 3f32b12bb..568096896 100644 --- a/src/kernel/spellbook.c +++ b/src/kernel/spellbook.c @@ -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; } } diff --git a/src/kernel/spellbook.h b/src/kernel/spellbook.h index f4724ec5b..6e5cc5ea3 100644 --- a/src/kernel/spellbook.h +++ b/src/kernel/spellbook.h @@ -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 } diff --git a/src/kernel/spellbook_test.c b/src/kernel/spellbook_test.c index 62d8fa5ea..29b3bd4b7 100644 --- a/src/kernel/spellbook_test.c +++ b/src/kernel/spellbook_test.c @@ -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); diff --git a/src/kernel/version.h b/src/kernel/version.h index c179b99fb..b7ec62b01 100644 --- a/src/kernel/version.h +++ b/src/kernel/version.h @@ -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 */ diff --git a/src/kernel/xmlreader.c b/src/kernel/xmlreader.c index 3f7422488..bdeba293e 100644 --- a/src/kernel/xmlreader.c +++ b/src/kernel/xmlreader.c @@ -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))