diff --git a/clibs b/clibs index da2c0cc39..147584ad7 160000 --- a/clibs +++ b/clibs @@ -1 +1 @@ -Subproject commit da2c0cc39b27c98ed8d31b0503426788fc236bd8 +Subproject commit 147584ad70b220abf6a4e97ca76e785729b9ac32 diff --git a/src/bind_unit.c b/src/bind_unit.c index 1d6579b0e..1e1aee00e 100644 --- a/src/bind_unit.c +++ b/src/bind_unit.c @@ -273,7 +273,7 @@ static int tolua_unit_set_guard(lua_State * L) static const char *unit_getmagic(const unit * u) { - sc_mage *mage = get_mage(u); + sc_mage *mage = get_mage_depr(u); return mage ? magic_school[mage->magietyp] : NULL; } @@ -286,7 +286,7 @@ static int tolua_unit_get_magic(lua_State * L) static void unit_setmagic(unit * u, const char *type) { - sc_mage *mage = get_mage(u); + sc_mage *mage = get_mage_depr(u); int mtype; for (mtype = 0; mtype != MAXMAGIETYP; ++mtype) { if (strcmp(magic_school[mtype], type) == 0) @@ -638,8 +638,14 @@ static int tolua_unit_get_familiar(lua_State * L) static int tolua_unit_set_familiar(lua_State * L) { - unit *self = (unit *)tolua_tousertype(L, 1, 0); - create_newfamiliar(self, (unit *)tolua_tousertype(L, 2, 0)); + unit *mag = (unit *)tolua_tousertype(L, 1, NULL); + unit *fam = (unit *)tolua_tousertype(L, 2, NULL); + if (fam) { + set_familiar(mag, fam); + } + else { + remove_familiar(mag); + } return 0; } @@ -747,7 +753,7 @@ static int tolua_unit_get_items(lua_State * L) static int tolua_unit_get_spells(lua_State * L) { unit *self = (unit *) tolua_tousertype(L, 1, 0); - sc_mage *mage = self ? get_mage(self) : 0; + sc_mage *mage = self ? get_mage_depr(self) : 0; spellbook *sb = mage ? mage->spellbook : 0; selist *slist = 0; if (sb) { diff --git a/src/creport.c b/src/creport.c index b68f3e4fe..4ec7f9b0b 100644 --- a/src/creport.c +++ b/src/creport.c @@ -954,7 +954,7 @@ void cr_output_unit(stream *out, const region * r, const faction * f, } /* spells that this unit can cast */ - mage = get_mage(u); + mage = get_mage_depr(u); if (mage) { int i, maxlevel = effskill(u, SK_MAGIC, 0); cr_output_spells(out, u, maxlevel); diff --git a/src/give.c b/src/give.c index 18d355031..da75e56c5 100644 --- a/src/give.c +++ b/src/give.c @@ -591,7 +591,7 @@ void give_unit(unit * u, unit * u2, order * ord) cmistake(u, ord, 155, MSG_COMMERCE); return; } - mage = get_mage(u); + mage = get_mage_depr(u); if (!mage || u2->faction->magiegebiet != mage->magietyp) { cmistake(u, ord, 157, MSG_COMMERCE); return; diff --git a/src/kernel/equipment.c b/src/kernel/equipment.c index b87f401dd..f24d4acfd 100644 --- a/src/kernel/equipment.c +++ b/src/kernel/equipment.c @@ -121,7 +121,7 @@ void equip_unit_mask(struct unit *u, const struct equipment *eq, int mask) if (eq->spells) { selist * ql = eq->spells; int qi; - sc_mage * mage = get_mage(u); + sc_mage * mage = get_mage_depr(u); for (qi = 0; ql; selist_advance(&ql, &qi, 1)) { lazy_spell *sbe = (lazy_spell *)selist_get(ql, qi); diff --git a/src/kernel/equipment.test.c b/src/kernel/equipment.test.c index 7952001fe..0e0ebf196 100644 --- a/src/kernel/equipment.test.c +++ b/src/kernel/equipment.test.c @@ -39,7 +39,7 @@ static void test_equipment(CuTest * tc) CuAssertIntEquals(tc, 1, i_get(u->items, it_horses)); CuAssertIntEquals(tc, 5, get_level(u, SK_MAGIC)); - mage = get_mage(u); + mage = get_mage_depr(u); CuAssertPtrNotNull(tc, mage); CuAssertPtrNotNull(tc, mage->spellbook); CuAssertTrue(tc, u_hasspell(u, sp)); diff --git a/src/kernel/messages.c b/src/kernel/messages.c index b8d33f08c..f2a175269 100644 --- a/src/kernel/messages.c +++ b/src/kernel/messages.c @@ -83,7 +83,7 @@ struct message *msg_feedback(const struct unit *u, struct order *ord, variant var; memset(args, 0, sizeof(args)); - if (ord && ord->_noerror) { + if (ord && is_silent(ord)) { return NULL; } diff --git a/src/kernel/messages.test.c b/src/kernel/messages.test.c index cd301756d..a26530895 100644 --- a/src/kernel/messages.test.c +++ b/src/kernel/messages.test.c @@ -79,8 +79,8 @@ static void test_noerror(CuTest *tc) { lang = test_create_locale(); u = test_create_unit(test_create_faction(NULL), test_create_region(0, 0, NULL)); u->thisorder = parse_order("!@move", lang); - CuAssertTrue(tc, u->thisorder->_persistent); - CuAssertTrue(tc, u->thisorder->_noerror); + CuAssertIntEquals(tc, K_MOVE | CMD_QUIET | CMD_PERSIST, u->thisorder->command); + CuAssertTrue(tc, !is_persistent(u->thisorder)); CuAssertPtrEquals(tc, NULL, msg_error(u, u->thisorder, 100)); CuAssertPtrEquals(tc, NULL, msg_feedback(u, u->thisorder, "error_unit_not_found", NULL)); test_cleanup(); diff --git a/src/kernel/order.c b/src/kernel/order.c index d64da0131..a48825333 100644 --- a/src/kernel/order.c +++ b/src/kernel/order.c @@ -30,12 +30,12 @@ #include #include -# define ORD_KEYWORD(ord) (ord)->data->_keyword +# define ORD_KEYWORD(ord) (keyword_t)((ord)->command & 0xFFFF) # define ORD_LOCALE(ord) locale_array[(ord)->data->_lindex]->lang # define ORD_STRING(ord) (ord)->data->_str typedef struct locale_data { - struct order_data *short_orders[MAXKEYWORDS]; + struct order_data *short_orders; struct order_data *study_orders[MAXSKILLS]; const struct locale *lang; } locale_data; @@ -46,7 +46,6 @@ typedef struct order_data { const char *_str; int _refcount; int _lindex; - keyword_t _keyword; } order_data; static void release_data(order_data * data) @@ -94,7 +93,7 @@ char* get_command(const order *ord, char *sbuffer, size_t size) { keyword_t kwd = ORD_KEYWORD(ord); int bytes; - if (ord->_noerror) { + if (ord->command & CMD_QUIET) { if (size > 0) { *bufp++ = '!'; --size; @@ -103,7 +102,7 @@ char* get_command(const order *ord, char *sbuffer, size_t size) { WARN_STATIC_BUFFER(); } } - if (ord->_persistent) { + if (ord->command & CMD_PERSIST) { if (size > 0) { *bufp++ = '@'; --size; @@ -162,8 +161,7 @@ order *copy_order(const order * src) if (src != NULL) { order *ord = (order *)malloc(sizeof(order)); ord->next = NULL; - ord->_persistent = src->_persistent; - ord->_noerror = src->_noerror; + ord->command = src->command; ord->data = src->data; ++ord->data->_refcount; return ord; @@ -189,13 +187,12 @@ void free_orders(order ** olist) } } -static char *mkdata(order_data **pdata, size_t len, keyword_t kwd, int lindex, const char *str) +static char *mkdata(order_data **pdata, size_t len, int lindex, const char *str) { order_data *data; char *result; data = malloc(sizeof(order_data) + len + 1); result = (char *)(data + 1); - data->_keyword = kwd; data->_lindex = lindex; data->_refcount = 0; data->_str = 0; @@ -229,7 +226,7 @@ static order_data *create_data(keyword_t kwd, const char *sptr, int lindex) const char *skname = skillname(sk, lang); const char *spc = strchr(skname, ' '); size_t len = strlen(skname); - char *dst = mkdata(&data, len + (spc ? 3 : 0), kwd, lindex, spc ? 0 : skname); + char *dst = mkdata(&data, len + (spc ? 3 : 0), lindex, spc ? 0 : skname); locale_array[lindex]->study_orders[sk] = data; if (spc) { dst[0] = '\"'; @@ -246,26 +243,24 @@ static order_data *create_data(keyword_t kwd, const char *sptr, int lindex) /* orders with no parameter, only one order_data per order required */ else if (kwd != NOKEYWORD && *sptr == 0) { - data = locale_array[lindex]->short_orders[kwd]; + data = locale_array[lindex]->short_orders; if (data == NULL) { - mkdata(&data, 0, kwd, lindex, 0); + mkdata(&data, 0, lindex, 0); data->_refcount = 1; - locale_array[lindex]->short_orders[kwd] = data; + locale_array[lindex]->short_orders = data; } ++data->_refcount; return data; } - mkdata(&data, s ? strlen(s) : 0, kwd, lindex, s); + mkdata(&data, s ? strlen(s) : 0, lindex, s); data->_refcount = 1; return data; } static void clear_localedata(int lindex) { int i; - for (i = 0; i != MAXKEYWORDS; ++i) { - release_data(locale_array[lindex]->short_orders[i]); - locale_array[lindex]->short_orders[i] = 0; - } + release_data(locale_array[lindex]->short_orders); + locale_array[lindex]->short_orders = NULL; for (i = 0; i != MAXSKILLS; ++i) { release_data(locale_array[lindex]->study_orders[i]); locale_array[lindex]->study_orders[i] = 0; @@ -284,12 +279,12 @@ void close_orders(void) { } } -static order *create_order_i(keyword_t kwd, const char *sptr, bool persistent, +static order *create_order_i(order *ord, keyword_t kwd, const char *sptr, bool persistent, bool noerror, const struct locale *lang) { - order *ord = NULL; int lindex; + assert(ord); if (kwd == NOKEYWORD || keyword_disabled(kwd)) { log_error("trying to create an order for disabled keyword %s.", keyword(kwd)); return NULL; @@ -316,11 +311,12 @@ static order *create_order_i(keyword_t kwd, const char *sptr, bool persistent, } locale_array[lindex]->lang = lang; - ord = (order *)malloc(sizeof(order)); - ord->_persistent = persistent; - ord->_noerror = noerror; + ord->command = (int)kwd; + if (persistent) ord->command |= CMD_PERSIST; + if (noerror) ord->command |= CMD_QUIET; ord->next = NULL; + while (isspace(*(unsigned char *)sptr)) ++sptr; ord->data = create_data(kwd, sptr, lindex); return ord; @@ -329,6 +325,7 @@ static order *create_order_i(keyword_t kwd, const char *sptr, bool persistent, order *create_order(keyword_t kwd, const struct locale * lang, const char *params, ...) { + order *ord; char zBuffer[DISPLAYSIZE]; if (params) { char *bufp = zBuffer; @@ -379,42 +376,40 @@ order *create_order(keyword_t kwd, const struct locale * lang, else { zBuffer[0] = 0; } - return create_order_i(kwd, zBuffer, false, false, lang); + ord = (order *)malloc(sizeof(order)); + return create_order_i(ord, kwd, zBuffer, false, false, lang); } order *parse_order(const char *s, const struct locale * lang) { assert(lang); assert(s); - while (*s && !isalnum(*(unsigned char *)s) && !ispunct(*(unsigned char *)s)) { - ++s; - } if (*s != 0) { - keyword_t kwd; - const char *sptr; + keyword_t kwd = NOKEYWORD; + const char *sptr = s; bool persistent = false, noerror = false; const char * p; - while (*s == '!' || *s=='@') { - if (*s=='!') noerror = true; - else if (*s == '@') persistent = true; - ++s; - } - sptr = s; p = *sptr ? parse_token_depr(&sptr) : 0; - kwd = p ? get_keyword(p, lang) : NOKEYWORD; + if (p) { + while (*p == '!' || *p == '@') { + if (*p == '!') noerror = true; + else if (*p == '@') persistent = true; + ++p; + } + kwd = get_keyword(p, lang); + } if (kwd == K_MAKE) { - const char *s, *sp = sptr; - s = parse_token_depr(&sp); - if (s && isparam(s, lang, P_TEMP)) { + const char *sp = sptr; + p = parse_token_depr(&sp); + if (p && isparam(p, lang, P_TEMP)) { kwd = K_MAKETEMP; sptr = sp; } } if (kwd != NOKEYWORD) { - while (isspace(*(unsigned char *)sptr)) ++sptr; - s = sptr; - return create_order_i(kwd, s, persistent, noerror, lang); + order *ord = (order *)malloc(sizeof(order)); + return create_order_i(ord, kwd, sptr, persistent, noerror, lang); } } return NULL; @@ -561,10 +556,15 @@ bool is_persistent(const order * ord) case K_KOMMENTAR: return true; default: - return ord->_persistent || is_repeated(kwd); + return (ord->command & CMD_PERSIST) || is_repeated(kwd); } } +bool is_silent(const order * ord) +{ + return (ord->command & CMD_QUIET) != 0; +} + char *write_order(const order * ord, char *buffer, size_t size) { if (ord == 0) { @@ -595,5 +595,5 @@ keyword_t init_order(const struct order *ord) { assert(ord && ord->data); init_tokens_str(ord->data->_str); - return ord->data->_keyword; + return ORD_KEYWORD(ord); } diff --git a/src/kernel/order.h b/src/kernel/order.h index bf4d02982..b9d235c88 100644 --- a/src/kernel/order.h +++ b/src/kernel/order.h @@ -32,32 +32,36 @@ extern "C" { struct order_data; +#define CMD_QUIET 0x010000 +#define CMD_PERSIST 0x020000 +#define CMD_DEFAULT 0x040000 + typedef struct order { struct order *next; /* do not access this data: */ struct order_data *data; - bool _persistent; - bool _noerror; + int command; } order; /* constructor */ - extern order *create_order(keyword_t kwd, const struct locale *lang, + order *create_order(keyword_t kwd, const struct locale *lang, const char *params, ...); - extern order *parse_order(const char *s, const struct locale *lang); - extern void replace_order(order ** dst, order * orig, const order * src); + order *parse_order(const char *s, const struct locale *lang); + void replace_order(order ** dst, order * orig, const order * src); /* reference counted copies of orders: */ - extern order *copy_order(const order * ord); - extern void free_order(order * ord); - extern void free_orders(order ** olist); + order *copy_order(const order * ord); + void free_order(order * ord); + void free_orders(order ** olist); - extern void push_order(struct order **olist, struct order *ord); + void push_order(struct order **olist, struct order *ord); /* access functions for orders */ keyword_t getkeyword(const order * ord); void set_order(order ** destp, order * src); char* get_command(const order *ord, char *buffer, size_t size); bool is_persistent(const order * ord); + bool is_silent(const order * ord); bool is_exclusive(const order * ord); bool is_repeated(keyword_t kwd); bool is_long(keyword_t kwd); diff --git a/src/kernel/order.test.c b/src/kernel/order.test.c index 876d75b1e..71938ca7e 100644 --- a/src/kernel/order.test.c +++ b/src/kernel/order.test.c @@ -38,8 +38,7 @@ static void test_parse_order(CuTest *tc) { ord = parse_order("MOVE NORTH", lang); CuAssertPtrNotNull(tc, ord); - CuAssertTrue(tc, !ord->_noerror); - CuAssertTrue(tc, !ord->_persistent); + CuAssertIntEquals(tc, K_MOVE, ord->command); CuAssertIntEquals(tc, K_MOVE, getkeyword(ord)); CuAssertStrEquals(tc, "move NORTH", get_command(ord, cmd, sizeof(cmd))); @@ -49,26 +48,37 @@ static void test_parse_order(CuTest *tc) { ord = parse_order("!MOVE NORTH", lang); CuAssertPtrNotNull(tc, ord); - CuAssertTrue(tc, ord->_noerror); - CuAssertTrue(tc, !ord->_persistent); + CuAssertPtrNotNull(tc, ord->data); + CuAssertIntEquals(tc, K_MOVE, getkeyword(ord)); + CuAssertIntEquals(tc, K_MOVE | CMD_QUIET, ord->command); free_order(ord); ord = parse_order("@MOVE NORTH", lang); CuAssertPtrNotNull(tc, ord); - CuAssertTrue(tc, !ord->_noerror); - CuAssertTrue(tc, ord->_persistent); - free_order(ord); - - ord = parse_order("@!MOVE NORTH", lang); - CuAssertPtrNotNull(tc, ord); - CuAssertTrue(tc, ord->_noerror); - CuAssertTrue(tc, ord->_persistent); + CuAssertPtrNotNull(tc, ord->data); + CuAssertIntEquals(tc, K_MOVE, getkeyword(ord)); + CuAssertIntEquals(tc, K_MOVE | CMD_PERSIST, ord->command); free_order(ord); ord = parse_order("!@MOVE NORTH", lang); CuAssertPtrNotNull(tc, ord); - CuAssertTrue(tc, ord->_noerror); - CuAssertTrue(tc, ord->_persistent); + CuAssertPtrNotNull(tc, ord->data); + CuAssertIntEquals(tc, K_MOVE, getkeyword(ord)); + CuAssertIntEquals(tc, K_MOVE | CMD_PERSIST | CMD_QUIET, ord->command); + free_order(ord); + + ord = parse_order("@!MOVE NORTH", lang); + CuAssertPtrNotNull(tc, ord); + CuAssertPtrNotNull(tc, ord->data); + CuAssertIntEquals(tc, K_MOVE, getkeyword(ord)); + CuAssertIntEquals(tc, K_MOVE | CMD_PERSIST | CMD_QUIET, ord->command); + free_order(ord); + + ord = parse_order(" !@MOVE NORTH", lang); + CuAssertPtrNotNull(tc, ord); + CuAssertPtrNotNull(tc, ord->data); + CuAssertIntEquals(tc, K_MOVE, getkeyword(ord)); + CuAssertIntEquals(tc, K_MOVE | CMD_PERSIST | CMD_QUIET, ord->command); free_order(ord); test_cleanup(); @@ -204,16 +214,84 @@ static void test_get_command(CuTest *tc) { lang = test_create_locale(); ord = create_order(K_MAKE, lang, "iron"); CuAssertStrEquals(tc, "make iron", get_command(ord, buf, sizeof(buf))); - ord->_noerror = true; + ord->command |= CMD_QUIET; CuAssertStrEquals(tc, "!make iron", get_command(ord, buf, sizeof(buf))); - ord->_persistent = true; + ord->command |= CMD_PERSIST; CuAssertStrEquals(tc, "!@make iron", get_command(ord, buf, sizeof(buf))); - ord->_noerror = false; + ord->command = K_MAKE | CMD_PERSIST; CuAssertStrEquals(tc, "@make iron", get_command(ord, buf, sizeof(buf))); free_order(ord); test_cleanup(); } +static void test_is_persistent(CuTest *tc) { + order *ord; + struct locale *lang; + + test_setup(); + lang = test_create_locale(); + + ord = parse_order("@invalid", lang); + CuAssertPtrEquals(tc, NULL, ord); + + ord = parse_order("give", lang); + CuAssertIntEquals(tc, K_GIVE, ord->command); + CuAssertTrue(tc, !is_persistent(ord)); + free_order(ord); + + ord = parse_order("@give", lang); + CuAssertTrue(tc, !is_repeated(K_GIVE)); + CuAssertIntEquals(tc, K_GIVE | CMD_PERSIST, ord->command); + CuAssertTrue(tc, is_persistent(ord)); + free_order(ord); + + ord = parse_order("make", lang); + CuAssertTrue(tc, is_repeated(K_MAKE)); + CuAssertIntEquals(tc, K_MAKE , ord->command); + CuAssertTrue(tc, is_persistent(ord)); + free_order(ord); + + ord = parse_order("@move", lang); + CuAssertIntEquals(tc, K_MOVE | CMD_PERSIST, ord->command); + CuAssertTrue(tc, !is_persistent(ord)); + free_order(ord); + + ord = parse_order("// comment", lang); + CuAssertTrue(tc, is_persistent(ord)); + CuAssertIntEquals(tc, K_KOMMENTAR, ord->command); + free_order(ord); + + test_cleanup(); +} + + +static void test_is_silent(CuTest *tc) { + order *ord; + struct locale *lang; + + test_setup(); + lang = test_create_locale(); + + ord = parse_order("make", lang); + CuAssertIntEquals(tc, K_MAKE, ord->command); + CuAssertTrue(tc, !is_silent(ord)); + free_order(ord); + + ord = parse_order("!make", lang); + CuAssertIntEquals(tc, K_MAKE | CMD_QUIET, ord->command); + CuAssertTrue(tc, is_silent(ord)); + free_order(ord); + + ord = parse_order("@invalid", lang); + CuAssertPtrEquals(tc, NULL, ord); + + ord = parse_order("// comment", lang); + CuAssertTrue(tc, is_persistent(ord)); + CuAssertIntEquals(tc, K_KOMMENTAR, ord->command); + free_order(ord); + + test_cleanup(); +} CuSuite *get_order_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -227,5 +305,7 @@ CuSuite *get_order_suite(void) SUITE_ADD_TEST(suite, test_skip_token); SUITE_ADD_TEST(suite, test_getstrtoken); SUITE_ADD_TEST(suite, test_get_command); + SUITE_ADD_TEST(suite, test_is_persistent); + SUITE_ADD_TEST(suite, test_is_silent); return suite; } diff --git a/src/kernel/save.c b/src/kernel/save.c index eb072c03d..6338b6827 100644 --- a/src/kernel/save.c +++ b/src/kernel/save.c @@ -24,8 +24,9 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "alchemy.h" #include "alliance.h" #include "ally.h" -#include "connection.h" #include "building.h" +#include "connection.h" +#include "equipment.h" #include "faction.h" #include "group.h" #include "item.h" @@ -1599,41 +1600,60 @@ static void fix_familiars(void) { if (u->_race != u->faction->race && (u->_race->flags & RCF_FAMILIAR)) { /* unit is potentially a familiar */ attrib * a = a_find(u->attribs, &at_mage); - if (a) { - /* unit is magical */ - attrib * am = a_find(u->attribs, &at_familiarmage); - if (!am) { - /* but it is not a familiar? */ - 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; - } + attrib * am = a_find(u->attribs, &at_familiarmage); + if (am) { + sc_mage *mage = a ? (sc_mage *)a->data.v : NULL; + /* a familiar */ + if (!mage) { + log_error("%s seems to be a familiar with no magic.", + unitname(u)); + mage = create_mage(u, M_GRAY); + } + if (!mage->spellbook) { + char eqname[32]; + equipment *eq; + + snprintf(eqname, sizeof(eqname), "fam_%s", u->_race->_name); + eq = get_equipment(eqname); + if (eq && eq->spells) { + log_error("%s seems to be a familiar with no spells.", + unitname(u)); + /* magical familiar, no spells */ + equip_unit_mask(u, eq, EQUIP_SPELLS); + } + } + } + else if (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); - } + } + 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 { - log_error("%s seems to be a broken familiar with no trigger.", unitname(u)); + set_familiar(um, u); } } + else { + log_error("%s seems to be a broken familiar with no trigger.", unitname(u)); + } } } } @@ -1790,7 +1810,7 @@ int read_game(gamedata *data) else { for (u = f->units; u; u = u->nextF) { if (data->version < SPELL_LEVEL_VERSION) { - sc_mage *mage = get_mage(u); + sc_mage *mage = get_mage_depr(u); if (mage) { faction *f = u->faction; int skl = effskill(u, SK_MAGIC, 0); diff --git a/src/kernel/unit.c b/src/kernel/unit.c index 2c6378ca1..674bdad08 100644 --- a/src/kernel/unit.c +++ b/src/kernel/unit.c @@ -1492,14 +1492,6 @@ unit *create_unit(region * r, faction * f, int number, const struct race *urace, if (f) { assert(faction_alive(f)); u_setfaction(u, f); - - if (f->locale) { - order *deford = default_order(f->locale); - if (deford) { - set_order(&u->thisorder, NULL); - addlist(&u->orders, deford); - } - } } set_number(u, number); @@ -1822,7 +1814,7 @@ void u_setrace(struct unit *u, const struct race *rc) void unit_add_spell(unit * u, sc_mage * m, struct spell * sp, int level) { - sc_mage *mage = m ? m : get_mage(u); + sc_mage *mage = m ? m : get_mage_depr(u); if (!mage) { log_debug("adding new spell %s to a previously non-mage unit %s\n", sp->sname, unitname(u)); @@ -1836,7 +1828,7 @@ void unit_add_spell(unit * u, sc_mage * m, struct spell * sp, int level) struct spellbook * unit_get_spellbook(const struct unit * u) { - sc_mage * mage = get_mage(u); + sc_mage * mage = get_mage_depr(u); if (mage) { if (mage->spellbook) { return mage->spellbook; diff --git a/src/kernel/unit.test.c b/src/kernel/unit.test.c index 2e4ce3568..1d2fc8124 100644 --- a/src/kernel/unit.test.c +++ b/src/kernel/unit.test.c @@ -274,7 +274,7 @@ static void test_skill_familiar(CuTest *tc) { CuAssertIntEquals(tc, 6, effskill(mag, SK_PERCEPTION, 0)); /* make them mage and familiar to each other */ - CuAssertIntEquals(tc, true, create_newfamiliar(mag, fam)); + create_newfamiliar(mag, fam); /* when they are in the same region, the mage gets half their skill as a bonus */ CuAssertIntEquals(tc, 6, effskill(fam, SK_PERCEPTION, 0)); @@ -287,33 +287,11 @@ static void test_skill_familiar(CuTest *tc) { test_cleanup(); } -static void test_age_familiar(CuTest *tc) { - unit *mag, *fam; - - test_cleanup(); - - mag = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); - fam = test_create_unit(mag->faction, test_create_region(0, 0, 0)); - CuAssertPtrEquals(tc, 0, get_familiar(mag)); - CuAssertPtrEquals(tc, 0, get_familiar_mage(fam)); - CuAssertIntEquals(tc, true, create_newfamiliar(mag, fam)); - CuAssertPtrEquals(tc, fam, get_familiar(mag)); - CuAssertPtrEquals(tc, mag, get_familiar_mage(fam)); - a_age(&fam->attribs, fam); - a_age(&mag->attribs, mag); - CuAssertPtrEquals(tc, fam, get_familiar(mag)); - CuAssertPtrEquals(tc, mag, get_familiar_mage(fam)); - set_number(fam, 0); - a_age(&mag->attribs, mag); - CuAssertPtrEquals(tc, 0, get_familiar(mag)); - test_cleanup(); -} - static void test_inside_building(CuTest *tc) { unit *u; building *b; - test_cleanup(); + test_setup(); u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); b = test_create_building(u->region, 0); @@ -631,7 +609,6 @@ CuSuite *get_unit_suite(void) SUITE_ADD_TEST(suite, test_skillmod); SUITE_ADD_TEST(suite, test_skill_hunger); SUITE_ADD_TEST(suite, test_skill_familiar); - SUITE_ADD_TEST(suite, test_age_familiar); SUITE_ADD_TEST(suite, test_inside_building); SUITE_ADD_TEST(suite, test_skills); SUITE_ADD_TEST(suite, test_limited_skills); diff --git a/src/laws.c b/src/laws.c index d7f73fa6c..f60af7f15 100644 --- a/src/laws.c +++ b/src/laws.c @@ -3007,10 +3007,100 @@ int checkunitnumber(const faction * f, int add) return 0; } +void maketemp_cmd(unit *u, order **olist) +{ + order *makeord; + int err = checkunitnumber(u->faction, 1); + + makeord = *olist; + if (err) { + if (err == 1) { + ADDMSG(&u->faction->msgs, + msg_feedback(u, makeord, + "too_many_units_in_alliance", + "allowed", maxunits(u->faction))); + } + else { + ADDMSG(&u->faction->msgs, + msg_feedback(u, makeord, + "too_many_units_in_faction", + "allowed", maxunits(u->faction))); + } + *olist = makeord->next; + makeord->next = NULL; + free_order(makeord); + while (*olist) { + keyword_t kwd; + order * ord = *olist; + *olist = ord->next; + ord->next = NULL; + kwd = getkeyword(ord); + free_order(ord); + if (kwd == K_END) { + break; + } + } + } + else { + char token[128]; + const char *s; + int alias; + ship *sh; + unit *u2; + order **ordp, **oinsert; +#ifndef NDEBUG + keyword_t kwd = init_order(makeord); + assert(kwd == K_MAKETEMP); +#endif + alias = getid(); + s = gettoken(token, sizeof(token)); + if (s && s[0] == '\0') { + /* empty name? => generate one */ + s = NULL; + } + u2 = create_unit(u->region, u->faction, 0, u->faction->race, alias, s, u); + fset(u2, UFL_ISNEW); + a_add(&u2->attribs, a_new(&at_alias))->data.i = alias; + sh = leftship(u); + if (sh) { + set_leftship(u2, sh); + } + setstatus(u2, u->status); + + /* copy orders until K_END from u to u2 */ + ordp = &makeord->next; + oinsert = &u2->orders; + + while (*ordp) { + order *ord = *ordp; + *ordp = ord->next; + if (getkeyword(ord) == K_END) { + ord->next = NULL; + free_order(ord); + break; + } + *oinsert = ord; + oinsert = &ord->next; + *oinsert = NULL; + } + *olist = *ordp; + makeord->next = NULL; + free_order(makeord); + + if (!u2->orders) { + order *deford = default_order(u2->faction->locale); + if (deford) { + set_order(&u2->thisorder, NULL); + addlist(&u2->orders, deford); + } + } + } +} + void new_units(void) { region *r; - unit *u, *u2; + unit *u; /* neue einheiten werden gemacht und ihre befehle (bis zum "ende" zu * ihnen rueberkopiert, damit diese einheiten genauso wie die alten @@ -3028,73 +3118,13 @@ void new_units(void) } while (*ordp) { - order *makeord = *ordp; - if (getkeyword(makeord) == K_MAKETEMP) { - char token[128], *name = NULL; - const char *s; - int alias; - ship *sh; - order **newordersp; - int err = checkunitnumber(u->faction, 1); - - if (err) { - if (err == 1) { - ADDMSG(&u->faction->msgs, - msg_feedback(u, makeord, - "too_many_units_in_alliance", - "allowed", maxunits(u->faction))); - } - else { - ADDMSG(&u->faction->msgs, - msg_feedback(u, makeord, - "too_many_units_in_faction", - "allowed", maxunits(u->faction))); - } - ordp = &makeord->next; - - while (*ordp) { - order *ord = *ordp; - if (getkeyword(ord) == K_END) - break; - *ordp = ord->next; - ord->next = NULL; - free_order(ord); - } - continue; - } - init_order(makeord); - alias = getid(); - - s = gettoken(token, sizeof(token)); - if (s && s[0]) { - name = strdup(s); - } - u2 = create_unit(r, u->faction, 0, u->faction->race, alias, name, u); - if (name != NULL) - free(name); /* TODO: use a buffer on the stack instead? */ - fset(u2, UFL_ISNEW); - - a_add(&u2->attribs, a_new(&at_alias))->data.i = alias; - sh = leftship(u); - if (sh) { - set_leftship(u2, sh); - } - setstatus(u2, u->status); - - ordp = &makeord->next; - newordersp = &u2->orders; - while (*ordp) { - order *ord = *ordp; - if (getkeyword(ord) == K_END) - break; - *ordp = ord->next; - ord->next = NULL; - *newordersp = ord; - newordersp = &ord->next; - } + order *ord = *ordp; + if (getkeyword(ord) == K_MAKETEMP) { + maketemp_cmd(u, ordp); + } + else { + ordp = &ord->next; } - if (*ordp == makeord) - ordp = &makeord->next; } } } @@ -3357,7 +3387,7 @@ static int faction_getmages(faction * f, unit ** results, int numresults) for (u = f->units; u; u = u->nextF) { if (u->number > 0) { - sc_mage *mage = get_mage(u); + sc_mage *mage = get_mage_depr(u); if (mage) { int level = effskill(u, SK_MAGIC, 0); if (level > maxlevel) { @@ -3416,7 +3446,7 @@ static void update_spells(void) show_new_spells(f, maxlevel, faction_get_spellbook(f)); for (i = 0; i != MAXMAGES && mages[i]; ++i) { unit * u = mages[i]; - sc_mage *mage = get_mage(u); + sc_mage *mage = get_mage_depr(u); if (mage && mage->spellbook) { int level = effskill(u, SK_MAGIC, 0); show_new_spells(f, level, mage->spellbook); diff --git a/src/laws.test.c b/src/laws.test.c index 9f7884fc5..ee5c55f19 100644 --- a/src/laws.test.c +++ b/src/laws.test.c @@ -428,6 +428,63 @@ static void test_unit_limit(CuTest * tc) test_cleanup(); } +static void test_maketemp(CuTest * tc) +{ + faction *f; + unit *u, *u2; + + test_setup(); + f = test_create_faction(NULL); + u = test_create_unit(f, test_create_region(0, 0, NULL)); + + u->orders = create_order(K_MAKETEMP, f->locale, "1"); + u->orders->next = create_order(K_ENTERTAIN, f->locale, NULL); + u->orders->next->next = create_order(K_END, f->locale, NULL); + u->orders->next->next->next = create_order(K_TAX, f->locale, NULL); + + new_units(); + CuAssertIntEquals(tc, 2, f->num_units); + CuAssertPtrNotNull(tc, u2 = u->next); + CuAssertPtrNotNull(tc, u2->orders); + CuAssertPtrEquals(tc, NULL, u2->orders->next); + CuAssertIntEquals(tc, K_ENTERTAIN, getkeyword(u2->orders)); + + CuAssertPtrNotNull(tc, u->orders); + CuAssertPtrEquals(tc, NULL, u->orders->next); + CuAssertIntEquals(tc, K_TAX, getkeyword(u->orders)); + test_cleanup(); +} + +static void test_maketemp_default_order(CuTest * tc) +{ + faction *f; + unit *u, *u2; + + test_setup(); + config_set("orders.default", "work"); + f = test_create_faction(NULL); + u = test_create_unit(f, test_create_region(0, 0, NULL)); + + new_units(); + CuAssertIntEquals(tc, 1, f->num_units); + + u->orders = create_order(K_MAKETEMP, f->locale, "1"); + u->orders->next = create_order(K_END, f->locale, NULL); + u->orders->next->next = create_order(K_TAX, f->locale, NULL); + + new_units(); + CuAssertIntEquals(tc, 2, f->num_units); + CuAssertPtrNotNull(tc, u2 = u->next); + CuAssertPtrNotNull(tc, u2->orders); + CuAssertPtrEquals(tc, NULL, u2->orders->next); + CuAssertIntEquals(tc, K_WORK, getkeyword(u2->orders)); + + CuAssertPtrNotNull(tc, u->orders); + CuAssertPtrEquals(tc, NULL, u->orders->next); + CuAssertIntEquals(tc, K_TAX, getkeyword(u->orders)); + test_cleanup(); +} + static void test_limit_new_units(CuTest * tc) { faction *f; @@ -449,6 +506,8 @@ static void test_limit_new_units(CuTest * tc) CuAssertPtrNotNull(tc, u->next); CuAssertIntEquals(tc, 2, f->num_units); + CuAssertPtrEquals(tc, NULL, u->orders); + u->orders = create_order(K_MAKETEMP, f->locale, "1"); new_units(); CuAssertIntEquals(tc, 2, f->num_units); CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "too_many_units_in_faction")); @@ -458,6 +517,8 @@ static void test_limit_new_units(CuTest * tc) config_set("rules.limit.faction", "3"); config_set("rules.limit.alliance", "2"); + CuAssertPtrEquals(tc, NULL, u->orders); + u->orders = create_order(K_MAKETEMP, f->locale, "1"); new_units(); CuAssertIntEquals(tc, 2, f->num_units); CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "too_many_units_in_alliance")); @@ -466,6 +527,8 @@ static void test_limit_new_units(CuTest * tc) u = test_create_unit(test_create_faction(NULL), u->region); setalliance(u->faction, al); + CuAssertPtrEquals(tc, NULL, u->orders); + u->orders = create_order(K_MAKETEMP, f->locale, "1"); new_units(); CuAssertIntEquals(tc, 2, f->num_units); CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "too_many_units_in_alliance")); @@ -1555,6 +1618,8 @@ static void test_armedmen(CuTest *tc) { CuSuite *get_laws_suite(void) { CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_maketemp_default_order); + SUITE_ADD_TEST(suite, test_maketemp); SUITE_ADD_TEST(suite, test_nmr_warnings); SUITE_ADD_TEST(suite, test_ally_cmd); SUITE_ADD_TEST(suite, test_name_cmd); diff --git a/src/magic.c b/src/magic.c index d41ce71e4..7ec220875 100644 --- a/src/magic.c +++ b/src/magic.c @@ -42,6 +42,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include #include #include @@ -322,10 +323,19 @@ attrib_type at_mage = { bool is_mage(const unit * u) { - return get_mage(u) != NULL; + return get_mage_depr(u) != NULL; } sc_mage *get_mage(const unit * u) +{ + attrib *a = a_find(u->attribs, &at_mage); + if (a) { + return (sc_mage *)a->data.v; + } + return NULL; +} + +sc_mage *get_mage_depr(const unit * u) { if (has_skill(u, SK_MAGIC)) { attrib *a = a_find(u->attribs, &at_mage); @@ -333,7 +343,7 @@ sc_mage *get_mage(const unit * u) return (sc_mage *)a->data.v; } } - return (sc_mage *)NULL; + return NULL; } /* ------------------------------------------------------------- */ @@ -506,7 +516,7 @@ int u_hasspell(const unit *u, const struct spell *sp) int get_combatspelllevel(const unit * u, int nr) { - sc_mage *m = get_mage(u); + sc_mage *m = get_mage_depr(u); assert(nr < MAXCOMBATSPELLS); if (m) { @@ -524,7 +534,7 @@ const spell *get_combatspell(const unit * u, int nr) sc_mage *m; assert(nr < MAXCOMBATSPELLS); - m = get_mage(u); + m = get_mage_depr(u); if (m) { return m->combatspells[nr].sp; } @@ -533,7 +543,7 @@ const spell *get_combatspell(const unit * u, int nr) void set_combatspell(unit * u, spell * sp, struct order *ord, int level) { - sc_mage *mage = get_mage(u); + sc_mage *mage = get_mage_depr(u); int i = -1; assert(mage || !"trying to set a combat spell for non-mage"); @@ -573,7 +583,7 @@ void unset_combatspell(unit * u, spell * sp) int nr = 0; int i; - m = get_mage(u); + m = get_mage_depr(u); if (!m) return; @@ -609,7 +619,7 @@ int get_spellpoints(const unit * u) { sc_mage *m; - m = get_mage(u); + m = get_mage_depr(u); if (!m) return 0; @@ -620,7 +630,7 @@ void set_spellpoints(unit * u, int sp) { sc_mage *m; - m = get_mage(u); + m = get_mage_depr(u); if (!m) return; @@ -637,7 +647,7 @@ int change_spellpoints(unit * u, int mp) sc_mage *m; int sp; - m = get_mage(u); + m = get_mage_depr(u); if (!m) { return 0; } @@ -656,7 +666,7 @@ static int get_spchange(const unit * u) { sc_mage *m; - m = get_mage(u); + m = get_mage_depr(u); if (!m) return 0; @@ -710,7 +720,7 @@ int change_maxspellpoints(unit * u, int csp) { sc_mage *m; - m = get_mage(u); + m = get_mage_depr(u); if (!m) { return 0; } @@ -728,7 +738,7 @@ int countspells(unit * u, int step) sc_mage *m; int count; - m = get_mage(u); + m = get_mage_depr(u); if (!m) return 0; @@ -1312,7 +1322,7 @@ bool fumble(region * r, unit * u, const spell * sp, int cast_grade) } /* CHAOSPATZERCHANCE 10 : +10% Chance zu Patzern */ - mage = get_mage(u); + mage = get_mage_depr(u); if (mage->magietyp == M_DRAIG) { fumble_chance += CHAOSPATZERCHANCE; } @@ -2181,10 +2191,8 @@ void set_familiar(unit * mage, unit * familiar) a = a->next; } if (a == NULL) { - attrib *an = a_add(&mage->attribs, a_new(&at_skillmod)); - skillmod_data *smd = (skillmod_data *)an->data.v; - smd->special = sm_familiar; - smd->skill = NOSKILL; + a = make_skillmod(NOSKILL, sm_familiar, 0.0, 0); + a_add(&mage->attribs, a); } a = a_find(mage->attribs, &at_familiar); @@ -2192,17 +2200,19 @@ void set_familiar(unit * mage, unit * familiar) a = a_add(&mage->attribs, a_new(&at_familiar)); a->data.v = familiar; } - else + else { assert(!a->data.v || a->data.v == familiar); - /* TODO: Diese Attribute beim Tod des Familiars entfernen: */ + } + /* TODO: Diese Attribute beim Tod des Familiars entfernen: */ a = a_find(familiar->attribs, &at_familiarmage); if (a == NULL) { a = a_add(&familiar->attribs, a_new(&at_familiarmage)); a->data.v = mage; } - else + else { assert(!a->data.v || a->data.v == mage); + } } void remove_familiar(unit * mage) @@ -2218,48 +2228,35 @@ void remove_familiar(unit * mage) while (a && a->type == &at_skillmod) { an = a->next; smd = (skillmod_data *)a->data.v; - if (smd->special == sm_familiar) + if (smd->special == sm_familiar) { a_remove(&mage->attribs, a); + } a = an; } } -bool create_newfamiliar(unit * mage, unit * familiar) +void create_newfamiliar(unit * mage, unit * fam) { - /* if the skill modifier for the mage does not yet exist, add it */ - attrib *a; - attrib *afam = a_find(mage->attribs, &at_familiar); - attrib *amage = a_find(familiar->attribs, &at_familiarmage); + /* skills and spells: */ + const struct equipment *eq; + char eqname[64]; + const race *rc = u_race(fam); - if (afam == NULL) { - afam = a_add(&mage->attribs, a_new(&at_familiar)); - } - afam->data.v = familiar; - if (amage == NULL) { - amage = a_add(&familiar->attribs, a_new(&at_familiarmage)); - } - amage->data.v = mage; + set_familiar(mage, fam); + snprintf(eqname, sizeof(eqname), "fam_%s", rc->_name); + eq = get_equipment(eqname); + if (eq != NULL) { + equip_unit(fam, eq); + } + else { + log_info("could not perform initialization for familiar %s.\n", rc->_name); + } /* TODO: Diese Attribute beim Tod des Familiars entfernen: */ /* Wenn der Magier stirbt, dann auch der Vertraute */ - add_trigger(&mage->attribs, "destroy", trigger_killunit(familiar)); + add_trigger(&mage->attribs, "destroy", trigger_killunit(fam)); /* Wenn der Vertraute stirbt, dann bekommt der Magier einen Schock */ - add_trigger(&familiar->attribs, "destroy", trigger_shock(mage)); - - a = a_find(mage->attribs, &at_skillmod); - while (a && a->type == &at_skillmod) { - skillmod_data *smd = (skillmod_data *)a->data.v; - if (smd->special == sm_familiar) - break; - a = a->next; - } - if (a == NULL) { - attrib *an = a_add(&mage->attribs, a_new(&at_skillmod)); - skillmod_data *smd = (skillmod_data *)an->data.v; - smd->special = sm_familiar; - smd->skill = NOSKILL; - } - return true; + add_trigger(&fam->attribs, "destroy", trigger_shock(mage)); } static void * resolve_familiar(int id, void *data) { diff --git a/src/magic.h b/src/magic.h index f5d2ff0f4..403dafdc6 100644 --- a/src/magic.h +++ b/src/magic.h @@ -221,6 +221,7 @@ extern "C" { /* macht die struct unit zu einem neuen Magier: legt die struct u->mage an * und initialisiert den Magiertypus mit mtyp. */ sc_mage *get_mage(const struct unit *u); + sc_mage *get_mage_depr(const struct unit *u); /* gibt u->mage zurück, bei nicht-Magiern *NULL */ bool is_mage(const struct unit *u); /* gibt true, wenn u->mage gesetzt. */ @@ -329,7 +330,7 @@ extern "C" { struct unit *get_clone(const struct unit *u); struct unit *get_clone_mage(const struct unit *u); void remove_familiar(struct unit *mage); - bool create_newfamiliar(struct unit *mage, struct unit *familiar); + void create_newfamiliar(struct unit *mage, struct unit *familiar); void create_newclone(struct unit *mage, struct unit *familiar); struct unit *has_clone(struct unit *mage); diff --git a/src/magic.test.c b/src/magic.test.c index c6866ce47..a10c02b7b 100644 --- a/src/magic.test.c +++ b/src/magic.test.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -477,9 +478,131 @@ static void test_illusioncastle(CuTest *tc) test_cleanup(); } +static void test_is_mage(CuTest *tc) { + unit *u; + sc_mage *mage; + + test_setup(); + u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); + CuAssertPtrEquals(tc, NULL, get_mage(u)); + CuAssertTrue(tc, !is_mage(u)); + set_level(u, SK_MAGIC, 1); + CuAssertTrue(tc, !is_mage(u)); + CuAssertPtrEquals(tc, NULL, get_mage(u)); + CuAssertPtrNotNull(tc, mage = create_mage(u, M_CERDDOR)); + CuAssertPtrEquals(tc, mage, get_mage(u)); + CuAssertTrue(tc, is_mage(u)); + test_cleanup(); +} + +static void test_get_mage(CuTest *tc) { + unit *u; + sc_mage *mage; + + test_setup(); + u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); + CuAssertPtrEquals(tc, NULL, get_mage(u)); + CuAssertPtrEquals(tc, NULL, get_mage_depr(u)); + CuAssertPtrNotNull(tc, mage = create_mage(u, M_CERDDOR)); + CuAssertPtrEquals(tc, mage, get_mage(u)); + CuAssertPtrEquals(tc, NULL, get_mage_depr(u)); + set_level(u, SK_MAGIC, 1); + CuAssertPtrEquals(tc, mage, get_mage(u)); + CuAssertPtrEquals(tc, mage, get_mage_depr(u)); + test_cleanup(); +} + +static void test_familiar_set(CuTest *tc) { + unit *mag, *fam; + + test_setup(); + + mag = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); + fam = test_create_unit(mag->faction, test_create_region(0, 0, 0)); + CuAssertPtrEquals(tc, NULL, get_familiar(mag)); + CuAssertPtrEquals(tc, NULL, get_familiar_mage(fam)); + CuAssertPtrEquals(tc, NULL, a_find(mag->attribs, &at_skillmod)); + set_familiar(mag, fam); + CuAssertPtrEquals(tc, fam, get_familiar(mag)); + CuAssertPtrEquals(tc, mag, get_familiar_mage(fam)); + CuAssertPtrNotNull(tc, a_find(mag->attribs, &at_skillmod)); + remove_familiar(mag); + CuAssertPtrEquals(tc, NULL, get_familiar(mag)); + CuAssertPtrEquals(tc, NULL, a_find(mag->attribs, &at_skillmod)); + test_cleanup(); +} + +static void test_familiar_age(CuTest *tc) { + unit *mag, *fam; + + test_setup(); + + mag = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); + fam = test_create_unit(mag->faction, test_create_region(0, 0, 0)); + set_familiar(mag, fam); + CuAssertPtrEquals(tc, fam, get_familiar(mag)); + CuAssertPtrEquals(tc, mag, get_familiar_mage(fam)); + a_age(&fam->attribs, fam); + a_age(&mag->attribs, mag); + CuAssertPtrEquals(tc, fam, get_familiar(mag)); + CuAssertPtrEquals(tc, mag, get_familiar_mage(fam)); + set_number(fam, 0); + a_age(&mag->attribs, mag); + CuAssertPtrEquals(tc, NULL, get_familiar(mag)); + test_cleanup(); +} + +static void test_familiar_equip(CuTest *tc) { + unit *mag, *u; + equipment *eq; + const item_type * itype; + spell *sp; + sc_mage * mage; + + test_setup(); + + itype = test_create_itemtype("horse"); + CuAssertPtrNotNull(tc, itype); + sp = create_spell("testspell"); + CuAssertPtrNotNull(tc, sp); + + eq = get_or_create_equipment("fam_human"); + equipment_setitem(eq, itype, "1"); + equipment_setskill(eq, SK_ENTERTAINMENT, "5"); + equipment_addspell(eq, sp->sname, 1); + + mag = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); + u = test_create_unit(mag->faction, test_create_region(0, 0, 0)); + set_familiar(mag, u); + create_newfamiliar(mag, u); + CuAssertIntEquals(tc, 1, i_get(u->items, itype)); + CuAssertIntEquals(tc, 5, get_level(u, SK_ENTERTAINMENT)); + CuAssertIntEquals(tc, 0, get_level(u, SK_MAGIC)); + + mage = get_mage(u); + CuAssertPtrNotNull(tc, mage); + CuAssertPtrNotNull(tc, mage->spellbook); + set_level(u, SK_MAGIC, 1); + CuAssertPtrEquals(tc, mage, get_mage_depr(u)); + CuAssertTrue(tc, u_hasspell(u, sp)); + + test_cleanup(); +} + +CuSuite *get_familiar_suite(void) +{ + CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_familiar_equip); + SUITE_ADD_TEST(suite, test_familiar_set); + SUITE_ADD_TEST(suite, test_familiar_age); + return suite; +} + CuSuite *get_magic_suite(void) { CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_is_mage); + SUITE_ADD_TEST(suite, test_get_mage); SUITE_ADD_TEST(suite, test_multi_cast); SUITE_ADD_TEST(suite, test_updatespells); SUITE_ADD_TEST(suite, test_spellbooks); diff --git a/src/modules/autoseed.c b/src/modules/autoseed.c index 343c742ee..52a5344cc 100644 --- a/src/modules/autoseed.c +++ b/src/modules/autoseed.c @@ -147,8 +147,7 @@ newfaction *read_newfactions(const char *filename) faction *f; char race[20], email[64], lang[8], password[16]; newfaction *nf, **nfi; - int bonus = 0, subscription = 0; - int alliance = 0; + int alliance = 0, subscription = 0; if (fgets(buf, sizeof(buf), F) == NULL) break; @@ -156,8 +155,8 @@ newfaction *read_newfactions(const char *filename) email[0] = '\0'; password[0] = '\0'; - if (sscanf(buf, "%54s %20s %8s %d %d %16s %d", email, race, lang, &bonus, - &subscription, password, &alliance) < 3) { + if (sscanf(buf, "%54s %20s %8s %16s %d %d", email, race, lang, + password, &subscription, &alliance) < 3) { break; } if (email[0] == '#') { @@ -228,7 +227,6 @@ newfaction *read_newfactions(const char *filename) } } nf->lang = get_locale(lang); - nf->bonus = bonus; assert(nf->race && nf->email && nf->lang); nfi = &newfactions; while (*nfi) { diff --git a/src/modules/autoseed.h b/src/modules/autoseed.h index 65bdccb7f..937a1f8b5 100644 --- a/src/modules/autoseed.h +++ b/src/modules/autoseed.h @@ -24,7 +24,6 @@ extern "C" { char *password; const struct locale *lang; const struct race *race; - int bonus; int subscription; bool oldregions; struct alliance *allies; diff --git a/src/move.c b/src/move.c index 33ed38126..3f899ac22 100644 --- a/src/move.c +++ b/src/move.c @@ -89,14 +89,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include -/* Bewegungsweiten: */ -#define BP_WALKING 4 -#define BP_RIDING 6 -#define BP_UNICORN 9 -#define BP_DRAGON 4 -#define BP_NORMAL 3 -#define BP_ROAD 2 - int *storms; typedef struct traveldir { @@ -265,7 +257,7 @@ get_transporters(const item * itm, int *p_animals, int *p_acap, int *p_vehicles, *p_acap = acap; } -static int ridingcapacity(unit * u) +static int ridingcapacity(const unit * u) { int vehicles = 0, vcap = 0; int animals = 0, acap = 0; @@ -433,7 +425,7 @@ bool canswim(unit * u) return false; } -static int canride(unit * u) +static int canride(const unit * u) { int horses = 0, maxhorses, unicorns = 0, maxunicorns; int skill = effskill(u, SK_RIDING, 0); @@ -460,17 +452,17 @@ static int canride(unit * u) if (!(u_race(u)->flags & RCF_HORSE) && ((horses == 0 && unicorns == 0) || horses > maxhorses || unicorns > maxunicorns)) { - return 0; + return BP_WALKING; } if (ridingcapacity(u) - eff_weight(u) >= 0) { if (horses == 0 && unicorns >= u->number && !(u_race(u)->flags & RCF_HORSE)) { - return 2; + return BP_UNICORN; } - return 1; + return BP_RIDING; } - return 0; + return BP_WALKING; } static bool cansail(const region * r, ship * sh) @@ -1399,9 +1391,9 @@ static void make_route(unit * u, order * ord, region_list ** routep) * Normalerweise verliert man 3 BP pro Region, bei Straßen nur 2 BP. * Außerdem: Wenn Einheit transportiert, nur halbe BP */ -static int movement_speed(unit * u) +int movement_speed(const unit * u) { - int mp = BP_WALKING; + int mp = 0; const race *rc = u_race(u); double dk = rc->speed; assert(u->number); @@ -1415,6 +1407,10 @@ static int movement_speed(unit * u) mp = BP_DRAGON; break; default: + mp = canride(u); + if (mp>=BP_RIDING) { + dk = 1.0; + } break; } @@ -1426,38 +1422,22 @@ static int movement_speed(unit * u) } } - switch (canride(u)) { - case 1: /* Pferd */ - mp = BP_RIDING; - break; + /* unicorn in inventory */ + if (u->number <= i_get(u->items, it_find("fairyboot"))) { + mp *= 2; + } - case 2: /* Einhorn */ - mp = BP_UNICORN; - break; - - default: - /* Siebenmeilentee */ - if (get_effect(u, oldpotiontype[P_FAST]) >= u->number) { - mp *= 2; - change_effect(u, oldpotiontype[P_FAST], -u->number); - } - - /* unicorn in inventory */ - if (u->number <= i_get(u->items, it_find("fairyboot"))) { - mp *= 2; - } - - /* Im Astralraum sind Tyb und Ill-Magier doppelt so schnell. - * Nicht kumulativ mit anderen Beschleunigungen! */ - if (mp * dk <= BP_WALKING * u_race(u)->speed && is_astral(u->region) - && is_mage(u)) { - sc_mage *mage = get_mage(u); - if (mage->magietyp == M_TYBIED || mage->magietyp == M_ILLAUN) { + /* Im Astralraum sind Tyb und Ill-Magier doppelt so schnell. + * Nicht kumulativ mit anderen Beschleunigungen! */ + if (mp * dk <= BP_WALKING * u_race(u)->speed && is_astral(u->region)) { + sc_mage *mage = get_mage(u); + if (mage && (mage->magietyp == M_TYBIED || mage->magietyp == M_ILLAUN)) { + if (has_skill(u, SK_MAGIC)) { mp *= 2; } } - break; } + return (int)(dk * mp); } @@ -2044,7 +2024,7 @@ static const region_list *travel_i(unit * u, const region_list * route_begin, const region_list * route_end, order * ord, int mode, follower ** followers) { region *r = u->region; - + int mp; if (u->building && !can_leave(u)) { cmistake(u, u->thisorder, 150, MSG_MOVE); return route_begin; @@ -2060,7 +2040,15 @@ static const region_list *travel_i(unit * u, const region_list * route_begin, cmistake(u, ord, 42, MSG_MOVE); return route_begin; } - route_end = cap_route(r, route_begin, route_end, movement_speed(u)); + + mp = movement_speed(u); + /* Siebenmeilentee */ + if (get_effect(u, oldpotiontype[P_FAST]) >= u->number) { + mp *= 2; + change_effect(u, oldpotiontype[P_FAST], -u->number); + } + + route_end = cap_route(r, route_begin, route_end, mp); route_end = travel_route(u, route_begin, route_end, ord, mode); if (u->flags&UFL_FOLLOWED) { diff --git a/src/move.h b/src/move.h index d5b07fef9..4b9c75749 100644 --- a/src/move.h +++ b/src/move.h @@ -36,6 +36,14 @@ extern "C" { extern struct attrib_type at_shiptrail; extern int *storms; + /* Bewegungsweiten: */ +#define BP_WALKING 4 +#define BP_RIDING 6 +#define BP_UNICORN 9 +#define BP_DRAGON 4 +#define BP_NORMAL 3 +#define BP_ROAD 2 + /* die Zahlen sind genau äquivalent zu den race Flags */ #define MV_CANNOTMOVE (1<<5) #define MV_FLY (1<<7) /* kann fliegen */ @@ -70,6 +78,7 @@ extern "C" { struct ship *move_ship(struct ship *sh, struct region *from, struct region *to, struct region_list *route); int walkingcapacity(const struct unit *u); + int movement_speed(const struct unit * u); void follow_unit(struct unit *u); struct unit *owner_buildingtyp(const struct region *r, const struct building_type *bt); diff --git a/src/move.test.c b/src/move.test.c index ab30300e8..c8fdb2436 100644 --- a/src/move.test.c +++ b/src/move.test.c @@ -522,9 +522,33 @@ static void test_ship_leave_trail(CuTest *tc) { test_cleanup(); } +static void test_movement_speed(CuTest *tc) { + unit * u; + race * rc; + const struct item_type *it_horse; + + test_setup(); + it_horse = test_create_horse(); + rc = test_create_race(NULL); + u = test_create_unit(test_create_faction(rc), test_create_region(0, 0, NULL)); + + rc->speed = 1.0; + CuAssertIntEquals(tc, BP_WALKING, movement_speed(u)); + + rc->speed = 2.0; + CuAssertIntEquals(tc, 2 * BP_WALKING, movement_speed(u)); + + set_level(u, SK_RIDING, 1); + i_change(&u->items, it_horse, 1); + CuAssertIntEquals(tc, BP_RIDING, movement_speed(u)); + + test_cleanup(); +} + CuSuite *get_move_suite(void) { CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_movement_speed); SUITE_ADD_TEST(suite, test_walkingcapacity); SUITE_ADD_TEST(suite, test_ship_not_allowed_in_coast); SUITE_ADD_TEST(suite, test_ship_leave_trail); diff --git a/src/report.c b/src/report.c index 2dac94011..c23a9466c 100644 --- a/src/report.c +++ b/src/report.c @@ -1090,6 +1090,9 @@ void report_region(struct stream *out, const region * r, faction * f) message *msg; if (owner != NULL) { + bytes = (int)strlcpy(bufp, " ", size); + if (wrptr(&bufp, &size, bytes) != 0) + WARN_STATIC_BUFFER(); msg = msg_message("nr_region_owner", "faction", owner); bytes = (int)nr_render(msg, f->locale, bufp, size, f); msg_release(msg); diff --git a/src/reports.c b/src/reports.c index 1182f89c9..3d762a4ce 100644 --- a/src/reports.c +++ b/src/reports.c @@ -819,8 +819,9 @@ spskill(char *buffer, size_t size, const struct locale * lang, bufp = STRLCPY(bufp, " ", size); if (sv->id == SK_MAGIC) { - sc_mage *mage = get_mage(u); - if (mage && mage->magietyp != M_GRAY) { + sc_mage *mage = get_mage_depr(u); + assert(mage); + if (mage->magietyp != M_GRAY) { bufp = STRLCPY(bufp, LOC(lang, mkname("school", magic_school[mage->magietyp])), size); bufp = STRLCPY(bufp, " ", size); diff --git a/src/spells.c b/src/spells.c index 9d5481a60..c9e914904 100644 --- a/src/spells.c +++ b/src/spells.c @@ -39,7 +39,6 @@ #include #include #include -#include #include #include #include @@ -513,32 +512,26 @@ static const race *select_familiar(const race * magerace, magic_t magiegebiet) /* ------------------------------------------------------------- */ /* der Vertraue des Magiers */ -static void make_familiar(unit * familiar, unit * mage) +static unit * make_familiar(unit * mage, region *r, const race *rc, const char *name) { - /* skills and spells: */ - const struct equipment *eq; - char eqname[64]; - const race * rc = u_race(familiar); - snprintf(eqname, sizeof(eqname), "fam_%s", rc->_name); - eq = get_equipment(eqname); - if (eq != NULL) { - equip_items(&familiar->items, eq); - } - else { - log_info("could not perform initialization for familiar %s.\n", rc->_name); - } + unit *fam; + + fam = create_unit(r, mage->faction, 1, rc, 0, name, mage); + setstatus(fam, ST_FLEE); + fset(fam, UFL_LOCKED); /* triggers: */ - create_newfamiliar(mage, familiar); + create_newfamiliar(mage, fam); /* Hitpoints nach Talenten korrigieren, sonst starten vertraute * mit Ausdauerbonus verwundet */ - familiar->hp = unit_max_hp(familiar); + fam->hp = unit_max_hp(fam); + + return fam; } static int sp_summon_familiar(castorder * co) { - unit *familiar; region *r = co_get_region(co); unit *mage = co->magician.u; int cast_level = co->level; @@ -586,10 +579,7 @@ static int sp_summon_familiar(castorder * co) msg = msg_message("familiar_name", "unit", mage); nr_render(msg, mage->faction->locale, zText, sizeof(zText), mage->faction); msg_release(msg); - familiar = create_unit(r, mage->faction, 1, rc, 0, zText, mage); - setstatus(familiar, ST_FLEE); - fset(familiar, UFL_LOCKED); - make_familiar(familiar, mage); + make_familiar(mage, r, rc, zText); dh = 0; dh1 = 0; @@ -740,7 +730,7 @@ static int sp_transferaura(castorder * co) int cast_level = co->level; spellparameter *pa = co->par; unit *u; - sc_mage *scm_dst, *scm_src = get_mage(mage); + sc_mage *scm_dst, *scm_src = get_mage_depr(mage); /* wenn kein Ziel gefunden, Zauber abbrechen */ if (pa->param[0]->flag == TARGET_NOTFOUND) @@ -754,7 +744,7 @@ static int sp_transferaura(castorder * co) /* Wieviel Transferieren? */ aura = pa->param[1]->data.i; u = pa->param[0]->data.u; - scm_dst = get_mage(u); + scm_dst = get_mage_depr(u); if (scm_dst == NULL) { /* "Zu dieser Einheit kann ich keine Aura uebertragen." */ @@ -5822,7 +5812,7 @@ int sp_permtransfer(castorder * co) change_maxspellpoints(mage, -aura); change_spellpoints(mage, -aura); - if (get_mage(tu)->magietyp == get_mage(mage)->magietyp) { + if (get_mage_depr(tu)->magietyp == get_mage_depr(mage)->magietyp) { change_maxspellpoints(tu, aura / 2); } else { @@ -5945,18 +5935,18 @@ int sp_stealaura(castorder * co) /* Zieleinheit */ u = pa->param[0]->data.u; - if (!get_mage(u)) { + if (!get_mage_depr(u)) { ADDMSG(&mage->faction->msgs, msg_message("stealaura_fail", "unit target", mage, u)); ADDMSG(&u->faction->msgs, msg_message("stealaura_fail_detect", "unit", u)); return 0; } - taura = (get_mage(u)->spellpoints * (rng_int() % (int)(3 * power) + 1)) / 100; + taura = (get_mage_depr(u)->spellpoints * (rng_int() % (int)(3 * power) + 1)) / 100; if (taura > 0) { - get_mage(u)->spellpoints -= taura; - get_mage(mage)->spellpoints += taura; + get_mage_depr(u)->spellpoints -= taura; + get_mage_depr(mage)->spellpoints += taura; /* sprintf(buf, "%s entzieht %s %d Aura.", unitname(mage), unitname(u), taura); */ ADDMSG(&mage->faction->msgs, msg_message("stealaura_success", diff --git a/src/spy.c b/src/spy.c index 837bc28ee..210be63e4 100644 --- a/src/spy.c +++ b/src/spy.c @@ -68,7 +68,7 @@ void spy_message(int spy, const unit * u, const unit * target) ADDMSG(&u->faction->msgs, msg_message("spyreport", "spy target status", u, target, status)); if (spy > 20) { - sc_mage *mage = get_mage(target); + sc_mage *mage = get_mage_depr(target); /* for mages, spells and magic school */ if (mage) { ADDMSG(&u->faction->msgs, msg_message("spyreport_mage", "spy target type", u, diff --git a/src/study.c b/src/study.c index 6949db22e..88550fc30 100644 --- a/src/study.c +++ b/src/study.c @@ -427,8 +427,8 @@ int teach_cmd(unit * teacher, struct order *ord) if (sk == SK_MAGIC) { /* ist der Magier schon spezialisiert, so versteht er nur noch * Lehrer seines Gebietes */ - sc_mage *mage1 = get_mage(teacher); - sc_mage *mage2 = get_mage(student); + sc_mage *mage1 = get_mage_depr(teacher); + sc_mage *mage2 = get_mage_depr(student); if (mage2 && mage1 && mage2->magietyp != M_GRAY && mage1->magietyp != mage2->magietyp) { if (feedback) { @@ -782,7 +782,7 @@ int study_cmd(unit * u, order * ord) } } else if (sk == SK_MAGIC) { - sc_mage *mage = get_mage(u); + sc_mage *mage = get_mage_depr(u); if (!mage) { mage = create_mage(u, u->faction->magiegebiet); } diff --git a/src/study.test.c b/src/study.test.c index 21f76ad4c..75c169b74 100644 --- a/src/study.test.c +++ b/src/study.test.c @@ -409,11 +409,11 @@ static void test_study_magic(CuTest *tc) { study_cmd(u, u->thisorder); CuAssertIntEquals(tc, M_GWYRRD, f->magiegebiet); CuAssertIntEquals(tc, 0, i_get(u->items, rtype->itype)); - CuAssertPtrNotNull(tc, get_mage(u)); + CuAssertPtrNotNull(tc, get_mage_depr(u)); CuAssertPtrEquals(tc, 0, test_find_messagetype(f->msgs, "error65")); - CuAssertIntEquals(tc, M_GWYRRD, get_mage(u)->magietyp); + CuAssertIntEquals(tc, M_GWYRRD, get_mage_depr(u)->magietyp); - /* the static cost array in study_cost prevents this test: + /* TODO: the static cost array in study_cost prevents this test: test_clear_messages(f); config_set("skills.cost.magic", "50"); i_change(&u->items, rtype->itype, 50); diff --git a/src/test_eressea.c b/src/test_eressea.c index 5361ddf5e..215bdd2c5 100644 --- a/src/test_eressea.c +++ b/src/test_eressea.c @@ -101,6 +101,7 @@ int RunAllTests(int argc, char *argv[]) ADD_SUITE(pool); ADD_SUITE(curse); ADD_SUITE(equipment); + ADD_SUITE(familiar); ADD_SUITE(item); ADD_SUITE(magic); ADD_SUITE(alchemy); diff --git a/src/tests.c b/src/tests.c index 49b5b7c05..7d20b59c4 100644 --- a/src/tests.c +++ b/src/tests.c @@ -39,7 +39,7 @@ struct race *test_create_race(const char *name) { - race *rc = rc_get_or_create(name); + race *rc = rc_get_or_create(name ? name : "smurf"); rc->maintenance = 10; rc->hitpoints = 20; rc->maxaura = 100; diff --git a/src/util/attrib.c b/src/util/attrib.c index b3b9c0a6e..b6214fdd6 100644 --- a/src/util/attrib.c +++ b/src/util/attrib.c @@ -396,7 +396,6 @@ int a_age(attrib ** p, void *owner) static critbit_tree cb_deprecated = { 0 }; - typedef struct deprecated_s { unsigned int hash; int(*reader)(attrib *, void *, struct gamedata *); diff --git a/src/util/gamedata.h b/src/util/gamedata.h index 82df55cc3..22c1d9052 100644 --- a/src/util/gamedata.h +++ b/src/util/gamedata.h @@ -39,7 +39,7 @@ /* unfinished: */ #define CRYPT_VERSION 400 /* passwords are encrypted */ -#define RELEASE_VERSION SKILLSORT_VERSION /* current datafile */ +#define RELEASE_VERSION LANDDISPLAY_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 */ diff --git a/tests/run-turn.sh b/tests/run-turn.sh index fd6eb63ee..ad2e0e6b8 100755 --- a/tests/run-turn.sh +++ b/tests/run-turn.sh @@ -18,7 +18,7 @@ assert_grep_count() { file=$1 expr=$2 expect=$3 -count=`grep -cE $expr $file` +count=`grep -cE "$expr" $file` [ $count -eq $expect ] || quit 1 "expected $expect counts of $expr in $file, got $count" echo "PASS: $expr is $expect" } @@ -33,7 +33,7 @@ setup cleanup VALGRIND=`which valgrind` SERVER=../Debug/eressea/eressea -set -e +#set -e if [ -n "$VALGRIND" ]; then SUPP=../share/ubuntu-12_04.supp SERVER="$VALGRIND --track-origins=yes --gen-suppressions=all --suppressions=$SUPP --error-exitcode=1 --leak-check=no $SERVER" @@ -62,5 +62,6 @@ assert_grep_count reports/185-heg.cr '"lighthouse";visibility' 6 assert_grep_count reports/185-heg.cr '"neighbour";visibility' 11 assert_grep_count reports/185-6rLo.cr '^EINHEIT' 2 assert_grep_count reports/185-6rLo.cr '^REGION' 13 +assert_grep_count reports/185-6rLo.cr "Befehl ist unbekannt" 0 echo "integration tests: PASS" cleanup