diff --git a/CHANGELOG.md b/CHANGELOG.md index fbc0d2a87..2feb777d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# 30.0 + + - Feuerwand erzeugt curses in den beiden Regionen. + - Feuerwand-Auflösung nicht mehr über aging borders implementiert. + - age_borders Mechanismus ist damit überflüssig. + - Dateiformat: Kein Magier mehr in wall_data + - Fluch brechen, Antimagiekristal und Zerstoere Magie können Feuerwand auflösen + - Fluch brechen benutzt jetzt den selben Code wie andere Antimagie + # 29.4 - Weg der Bäume kostet auch bei resistenten Zielen [Bug 3051] diff --git a/res/core/messages.xml b/res/core/messages.xml index 3b1063a96..0c71f8703 100644 --- a/res/core/messages.xml +++ b/res/core/messages.xml @@ -274,6 +274,12 @@ + + + + + + diff --git a/res/translations/messages.de.po b/res/translations/messages.de.po index 8fedeb9c2..7cb04c952 100644 --- a/res/translations/messages.de.po +++ b/res/translations/messages.de.po @@ -2217,6 +2217,10 @@ msgctxt "curseinfo" msgid "slave_1" msgstr "\"$unit($unit) wird noch $int($duration) $if($eq($duration,1), \"Woche\", \"Wochen\") unter unserem Bann stehen. ($int36($id))\"" +msgctxt "curseinfo" +msgid "firewall" +msgstr "\"Eine magische Feuerwand blockiert den Weg nach $direction($direction). ($int36($id))\"" + msgid "alliance::lost" msgstr "\"$alliance($alliance) scheidet aus dem Spiel aus, nachdem alle Tempel verloren gingen.\"" diff --git a/res/translations/messages.en.po b/res/translations/messages.en.po index 28ce1ab55..f04112326 100644 --- a/res/translations/messages.en.po +++ b/res/translations/messages.en.po @@ -2217,6 +2217,10 @@ msgctxt "curseinfo" msgid "slave_1" msgstr "\"$unit($unit) will be under our influence for $int($duration) more $if($eq($duration,1), \"week\", \"weeks\"). ($int36($id))\"" +msgctxt "curseinfo" +msgid "firewall" +msgstr "\"A wall of magical fire is blocking the way $direction($direction). ($int36($id))\"" + msgid "alliance::lost" msgstr "\"$alliance($alliance) has to leave the game after all their temples were lost.\"" diff --git a/scripts/tests/e2/spells.lua b/scripts/tests/e2/spells.lua index 0c204b855..f20a2f355 100644 --- a/scripts/tests/e2/spells.lua +++ b/scripts/tests/e2/spells.lua @@ -502,3 +502,31 @@ function test_charming() assert_equal(f2, u2.faction) end + +function test_firewall() + local r1 = region.create(0, 0, 'plain') + local r2 = region.create(1, 0, 'plain') + local f = faction.create('human') + local u = unit.create(f, r1, 1) + local u2 = unit.create(f, r1, 1) + local uno = u.id + + assert_true(u2.hp == u2.hp_max) + u.magic = 'draig' + u:set_skill('magic', 24) + u.aura = 1000 + u:add_spell('firewall') + + u:add_order("ZAUBERE STUFE 1 'Feuerwand' OST") + u2:set_orders("NACH OST") -- no effect yet in same week + process_orders() + assert_true(u2.hp == u2.hp_max) + u:clear_orders() + u2:set_orders("NACH WEST") -- should take damage now + assert_true(r1:has_border("firewall", directions.EAST)) + assert_true(r2:has_border("firewall", directions.WEST)) + process_orders() + assert_false(r1:has_border("firewall", directions.EAST)) + assert_false(r2:has_border("firewall", directions.WEST)) + assert_true(u2.hp < u2.hp_max) +end diff --git a/src/battle.h b/src/battle.h index 34b50a325..e62e22e43 100644 --- a/src/battle.h +++ b/src/battle.h @@ -38,7 +38,7 @@ union variant; #define FL_HEALING_USED 128 /* has used a healing potion */ typedef struct bfaction { - struct bfaction* next; + struct bfaction* next; /* TODO: stb_ds benutzen? */ struct faction* faction; bool attacker; } bfaction; diff --git a/src/bind_region.c b/src/bind_region.c index c442573df..782900075 100644 --- a/src/bind_region.c +++ b/src/bind_region.c @@ -9,6 +9,7 @@ #include #include #include +#include "kernel/connection.h" #include "kernel/direction.h" #include #include @@ -263,6 +264,28 @@ static int tolua_region_get_roads(lua_State * L) return 1; } +static int tolua_region_has_border(lua_State *L) +{ + region *r = (region *)tolua_tousertype(L, 1, NULL); + const char * bname = tolua_tostring(L, 2, NULL); + const border_type *btype = bname ? find_bordertype(bname) : NULL; + + if (btype) { + direction_t dir = (direction_t)tolua_tonumber(L, 3, 0); + region *r2 = rconnect(r, dir); + connection *b; + for (b = get_borders(r, r2); b; b = b->next) { + if (b->type == btype) { + lua_pushboolean(L, true); + return 1; + } + } + lua_pushboolean(L, false); + return 1; + } + return 0; +} + static int tolua_region_get_luxury(lua_State * L) { region *r = (region *)tolua_tousertype(L, 1, NULL); @@ -829,6 +852,7 @@ void tolua_region_open(lua_State * L) tolua_function(L, "next", tolua_region_get_next); tolua_variable(L, "adj", tolua_region_get_adj, NULL); tolua_variable(L, "roads", tolua_region_get_roads, NULL); + tolua_function(L, "has_border", tolua_region_has_border); tolua_variable(L, "luxury", &tolua_region_get_luxury, &tolua_region_set_luxury); diff --git a/src/helpers.c b/src/helpers.c index 9d47f831d..6690023c9 100644 --- a/src/helpers.c +++ b/src/helpers.c @@ -99,7 +99,7 @@ static void push_param(lua_State * L, char c, spellparameter* param) else if (c == 'r') tolua_pushusertype(L, param->data.sh, "region"); else if (c == 'c') - tolua_pushstring(L, param->data.s); + tolua_pushstring(L, param->data.xs); else { log_error("unsupported syntax %c.\n", c); lua_pushnil(L); diff --git a/src/items.c b/src/items.c index 234279d9a..6fbdf5f92 100644 --- a/src/items.c +++ b/src/items.c @@ -122,8 +122,12 @@ struct order *ord) /* Kann auch von Nichtmagiern benutzt werden, erzeugt eine * Antimagiezone, die zwei Runden bestehen bleibt */ static int -use_antimagiccrystal(unit * u, const struct item_type *itype, int amount, -struct order *ord) +use_antimagiccrystal( + unit * u, + const struct item_type *itype, + int amount, + struct order *ord +) { region *r = u->region; const resource_type *rt_crystal; @@ -166,7 +170,7 @@ struct order *ord) continue; } - force = destr_curse(c, effect, force); + force = reduce_curse(c, effect, force, r); if (c->vigour <= 0) { a_remove(&r->attribs, a); } diff --git a/src/kernel/connection.c b/src/kernel/connection.c index 4c305eb91..f5dfd43d1 100644 --- a/src/kernel/connection.c +++ b/src/kernel/connection.c @@ -6,6 +6,7 @@ #include "terrain.h" #include "unit.h" +#include #include #include @@ -132,6 +133,7 @@ connection *create_border(border_type * type, region * from, region * to) void erase_border(connection * b) { + assert(b); if (b->from && b->to) { connection **bp = get_borders_i(b->from, b->to); assert(*bp != NULL || !"error: connection is not registered"); @@ -304,33 +306,6 @@ attrib_type at_countdown = { a_readint }; -void age_borders(void) -{ - connection **deleted = NULL; - int i; - - for (i = 0; i != BORDER_MAXHASH; ++i) { - connection *bhash = borders[i]; - for (; bhash; bhash = bhash->nexthash) { - connection *b = bhash; - for (; b; b = b->next) { - if (b->type->age) { - if (b->type->age(b) == AT_AGE_REMOVE) { - arrput(deleted, b); - } - } - } - } - } - if (deleted) { - size_t qi, ql; - for (ql = arrlen(deleted), qi = 0; qi != ql; ++qi) { - erase_border(deleted[qi]); - } - arrfree(deleted); - } -} - /******** * implementation of a couple of borders. this shouldn't really be in here, so * let's keep it separate from the more general stuff above @@ -513,6 +488,14 @@ int read_borders(gamedata *data) if (type->read) { connection *b = NULL; + if (data->version < WALL_DATA_VERSION) { + /* remove legacy firewalls */ + if (type == &bt_firewall) { + log_info("removing legacy firewall in %s", regionname(from, NULL)); + b = &dummy; + b->data.v = NULL; + } + } if (data->version < FIX_SEAROADS_VERSION) { /* bug 2694: eliminate roads in oceans */ if (type->terrain_flags != 0 && type->terrain_flags != fval(from->terrain, type->terrain_flags)) { diff --git a/src/kernel/connection.h b/src/kernel/connection.h index 9d3255a1c..f20d09322 100644 --- a/src/kernel/connection.h +++ b/src/kernel/connection.h @@ -72,8 +72,6 @@ extern "C" { struct region *(*move) (const connection *, struct unit * u, struct region * from, struct region * to, bool routing); /* executed when the units traverses this connection */ - int(*age) (struct connection *); - /* return 0 if connection needs to be removed. >0 if still aging, <0 if not aging */ struct border_type *next; /* for internal use only */ } border_type; @@ -94,7 +92,6 @@ extern "C" { int read_borders(struct gamedata *store); void write_borders(struct storage *store); - void age_borders(void); /* provide default implementations for some member functions: */ void b_read(connection * b, struct gamedata *store); diff --git a/src/kernel/curse.c b/src/kernel/curse.c index 5b2dbcbe0..36c5d5a7c 100644 --- a/src/kernel/curse.c +++ b/src/kernel/curse.c @@ -104,14 +104,14 @@ int curse_age(attrib * a, void *owner) c_clearflag(c, CURSE_ISNEW); if ((c_flags(c) & CURSE_NOAGE) == 0) { - if (--c->duration <= 0) { - result = AT_AGE_REMOVE; - } if (c->type->age) { - if (c->type->age(c) == 0) { + if (c->type->age(c, owner) == 0) { result = AT_AGE_REMOVE; } } + else if (--c->duration <= 0) { + result = AT_AGE_REMOVE; + } } return result; } @@ -691,12 +691,12 @@ message *cinfo_simple(const void *obj, objtype_t typ, const struct curse * c, * die Kraft des Curse um die halbe Staerke der Antimagie reduziert. * Zurueckgegeben wird der noch unverbrauchte Rest von force. */ -double destr_curse(curse * c, int cast_level, double force) +double reduce_curse(curse * c, int cast_level, double force, void *curse_target) { if (cast_level < c->vigour) { /* Zauber ist nicht stark genug */ force -= c->vigour; if (c->type->change_vigour) { - c->type->change_vigour(c, -(cast_level + 1) / 2); + c->type->change_vigour(c, -(cast_level + 1) / 2, curse_target); } else { c->vigour -= (cast_level + 1) / 2.0; @@ -706,7 +706,7 @@ double destr_curse(curse * c, int cast_level, double force) if (force >= c->vigour) { /* reicht die Kraft noch aus? */ force -= c->vigour; if (c->type->change_vigour) { - c->type->change_vigour(c, -c->vigour); + c->type->change_vigour(c, -c->vigour, curse_target); } else { c->vigour = 0; diff --git a/src/kernel/curse.h b/src/kernel/curse.h index 7a9e978f5..9b2495c36 100644 --- a/src/kernel/curse.h +++ b/src/kernel/curse.h @@ -139,13 +139,13 @@ extern "C" { int mergeflags; struct message *(*curseinfo) (const void *, objtype_t, const struct curse *, int); - void(*change_vigour) (struct curse *, double); + void(*change_vigour) (struct curse *, double, void *); int(*read) (struct gamedata *data, struct curse *, void *target); int(*write) (struct storage *store, const struct curse *, const void *target); int(*cansee) (const struct faction *, const void *, objtype_t, const struct curse *, int); - int(*age) (struct curse *); + int(*age) (struct curse *, void *); void(*construct) (struct curse *); void(*destroy) (struct curse *); } curse_type; @@ -231,7 +231,7 @@ extern "C" { int curse_age(struct attrib *a, void *owner); - double destr_curse(struct curse *c, int cast_level, double force); + double reduce_curse(struct curse *c, int cast_level, double force, void *curse_target); bool is_cursed_with(const struct attrib *ap, const struct curse *c); diff --git a/src/kernel/gamedata.h b/src/kernel/gamedata.h index fad67bfc9..a3c73c035 100644 --- a/src/kernel/gamedata.h +++ b/src/kernel/gamedata.h @@ -59,8 +59,9 @@ #define SKILL_DAYS_VERSION 381 /* skills are stored as days, not weeks */ #define BORDER_ID_VERSION 382 /* borders no longer have an id, from turn 1376 */ #define SLAVE_DATA_VERSION 383 /* ct_slavery has curse-data */ +#define WALL_DATA_VERSION 384 /* wall_data no longer has mage stored in it */ -#define RELEASE_VERSION SLAVE_DATA_VERSION /* use for new datafiles */ +#define RELEASE_VERSION WALL_DATA_VERSION /* use for new datafiles */ #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/src/kernel/order.c b/src/kernel/order.c index dc63f71e9..66b1bd037 100644 --- a/src/kernel/order.c +++ b/src/kernel/order.c @@ -266,7 +266,7 @@ static int create_data(keyword_t kwd, const char *s, if (kwd == K_STUDY || kwd == K_AUTOSTUDY || kwd == K_FORGET) { const char * sptr = s; skill_t sk = findskill(parse_token_depr(&sptr), lang); - if (sk != SK_MAGIC && sk != NOSKILL) { + if (kwd == K_FORGET || (sk != SK_MAGIC && sk != NOSKILL)) { return ((int)sk)-100; } } @@ -652,3 +652,42 @@ order *default_order(const struct locale *lang) return NULL; } +bool translate_order(order *ord, const struct locale *from_lang, const struct locale *to_lang) +{ + (void)to_lang; + (void)from_lang; + + if (ord->id <= 0) { + /* no arguments, no translation needed */ + return true; + } + switch (getkeyword(ord)) { + case NOKEYWORD: + case K_ATTACK: + case K_BANNER: + case K_DRIVE: + case K_FOLLOW: + case K_GROUP: + case K_KOMMENTAR: + case K_MAIL: + case K_NUMBER: + case K_PASSWORD: + case K_PREFIX: + case K_RECRUIT: + case K_SPY: + case K_STEAL: + case K_TEACH: + case K_TRANSPORT: + case K_URSPRUNG: + /* we can keep these, they do not use translated strings */ + return true; + case K_FORGET: + case K_AUTOSTUDY: + case K_STUDY: + /* we can keep these, they do not use translated strings */ + return (ord->id < 0); + default: + return false; + } +} + diff --git a/src/kernel/order.h b/src/kernel/order.h index 4166d5e0d..1d374a912 100644 --- a/src/kernel/order.h +++ b/src/kernel/order.h @@ -1,87 +1,78 @@ -#ifndef KRNL_ORDER_H -#define KRNL_ORDER_H - +#pragma once #include #include #include -#ifdef __cplusplus -extern "C" { -#endif - - struct locale; - struct stream; +struct locale; +struct stream; - /* Encapsulation of an order - * - * This structure contains one order given by a unit. These used to be - * stored in string lists, but by storing them in order-structures, - * it is possible to use reference-counting on them, reduce string copies, - * and reduce overall memory usage by sharing strings between orders (not - * implemented yet) saving approx. 50% of all string-related memory. - */ +/* Encapsulation of an order + * + * This structure contains one order given by a unit. These used to be + * stored in string lists, but by storing them in order-structures, + * it is possible to use reference-counting on them, reduce string copies, + * and reduce overall memory usage by sharing strings between orders (not + * implemented yet) saving approx. 50% of all string-related memory. + */ #define CMD_QUIET 0x010000 #define CMD_PERSIST 0x020000 #define CMD_DEFAULT 0x040000 - typedef struct order_data { - const char *_str; - int _refcount; - } order_data; - - extern order_data *odata_load(int id); - extern int odata_save(order_data *od); - - void odata_create(order_data **pdata, size_t len, const char *str); - void odata_release(order_data * od); - void odata_addref(order_data *od); - - typedef struct order { - struct order *next; - /* do not access this data: */ - int id; - int command; - } order; - - /* constructor */ - order *create_order(keyword_t kwd, const struct locale *lang, - const char *params, ...); - 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: */ - order *copy_order(const order * ord); - void free_order(order * ord); - void free_orders(order ** olist); - - void push_order(struct order **olist, struct order *ord); - - /* access functions for orders */ - keyword_t getkeyword(const order * ord); - void set_keyword(order *ord, keyword_t kwd); - void set_order(order ** destp, order * src); - char* get_command(const order *ord, const struct locale *lang, - 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); - const char *crescape(const char *str, char *buffer, size_t size); - - char *write_order(const order * ord, const struct locale *lang, - char *buffer, size_t size); - int stream_order(struct stream *out, const struct order *ord, const struct locale *lang, bool escape); - keyword_t init_order(const struct order *ord, const struct locale *lang); - void reset_order(void); - - void close_orders(void); - - struct order *default_order(const struct locale *lang); - -#ifdef __cplusplus -} -#endif -#endif +typedef struct order_data { + const char *_str; + int _refcount; +} order_data; + +extern order_data *odata_load(int id); +extern int odata_save(order_data *od); + +void odata_create(order_data **pdata, size_t len, const char *str); +void odata_release(order_data * od); +void odata_addref(order_data *od); + +typedef struct order { + struct order *next; + /* do not access this data: */ + int id; + int command; +} order; + +/* constructor */ +order *create_order(keyword_t kwd, const struct locale *lang, + const char *params, ...); +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: */ +order *copy_order(const order * ord); +void free_order(order * ord); +void free_orders(order ** olist); + +void push_order(struct order **olist, struct order *ord); + +/* access functions for orders */ +keyword_t getkeyword(const order * ord); +void set_keyword(order *ord, keyword_t kwd); +void set_order(order ** destp, order * src); +char* get_command(const order *ord, const struct locale *lang, + 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); +const char *crescape(const char *str, char *buffer, size_t size); + +char *write_order(const order * ord, const struct locale *lang, + char *buffer, size_t size); +int stream_order(struct stream *out, const struct order *ord, const struct locale *lang, bool escape); +keyword_t init_order(const struct order *ord, const struct locale *lang); +void reset_order(void); + +void close_orders(void); + +struct order *default_order(const struct locale *lang); + +bool translate_order(order *ord, const struct locale *from_lang, const struct locale *to_lang); diff --git a/src/kernel/order.test.c b/src/kernel/order.test.c index 798443323..63c69864f 100644 --- a/src/kernel/order.test.c +++ b/src/kernel/order.test.c @@ -308,6 +308,30 @@ static void test_replace_order(CuTest *tc) { test_teardown(); } +static void test_translate_order(CuTest *tc) { + order *ord; + struct locale *from_lang, *to_lang; + + test_setup(); + from_lang = test_create_locale(); + to_lang = test_create_locale(); + ord = create_order(K_WORK, from_lang, NULL); + CuAssertIntEquals(tc, K_WORK, ord->command); + CuAssertIntEquals(tc, 0, ord->id); + CuAssertTrue(tc, translate_order(ord, from_lang, to_lang)); + CuAssertIntEquals(tc, K_WORK, ord->command); + CuAssertIntEquals(tc, 0, ord->id); + + ord = create_order(K_STUDY, from_lang, skillname(SK_ENTERTAINMENT, from_lang)); + CuAssertIntEquals(tc, K_STUDY, ord->command); + CuAssertIntEquals(tc, (int)SK_ENTERTAINMENT - 100, ord->id); + CuAssertTrue(tc, translate_order(ord, from_lang, to_lang)); + CuAssertIntEquals(tc, K_STUDY, ord->command); + CuAssertIntEquals(tc, (int)SK_ENTERTAINMENT - 100, ord->id); + free_order(ord); + test_teardown(); +} + static void test_get_command(CuTest *tc) { struct locale * lang; order *ord; @@ -651,6 +675,7 @@ CuSuite *get_order_suite(void) SUITE_ADD_TEST(suite, test_parse_maketemp); SUITE_ADD_TEST(suite, test_init_order); SUITE_ADD_TEST(suite, test_replace_order); + SUITE_ADD_TEST(suite, test_translate_order); SUITE_ADD_TEST(suite, test_skip_token); SUITE_ADD_TEST(suite, test_getstrtoken); SUITE_ADD_TEST(suite, test_get_command); diff --git a/src/kernel/unit.c b/src/kernel/unit.c index 31733e4c2..2288dffb3 100644 --- a/src/kernel/unit.c +++ b/src/kernel/unit.c @@ -534,16 +534,18 @@ int read_unit_reference(gamedata * data, unit **up, resolve_fun fun) { int id; READ_INT(data->store, &id); - if (id > 0) { - *up = findunit(id); - if (*up == NULL) { + if (up) { + if (id > 0) { + *up = findunit(id); + if (*up == NULL) { + *up = NULL; + ur_add(RESOLVE_UNIT | id, (void **)up, fun); + } + } + else { *up = NULL; - ur_add(RESOLVE_UNIT | id, (void **)up, fun); } } - else { - *up = NULL; - } return id; } @@ -1837,40 +1839,6 @@ void unit_convert_race(unit *u, const race *rc, const char *rcname) } } -bool translate_order(order *ord, const struct locale *from_lang, const struct locale *to_lang) -{ - (void)to_lang; - (void)from_lang; - - if (ord->id <= 0) { - /* no arguments, no translation needed */ - return true; - } - switch (getkeyword(ord)) { - case NOKEYWORD: - case K_ATTACK: - case K_BANNER: - case K_DRIVE: - case K_FOLLOW: - case K_GROUP: - case K_KOMMENTAR: - case K_MAIL: - case K_NUMBER: - case K_PASSWORD: - case K_PREFIX: - case K_RECRUIT: - case K_SPY: - case K_STEAL: - case K_TEACH: - case K_TRANSPORT: - case K_URSPRUNG: - /* we can keep these, they do not use translated strings */ - return true; - default: - return false; - } -} - void translate_orders(unit *u, const struct locale *lang, order **list, bool del) { order **po = list; diff --git a/src/laws.c b/src/laws.c index bf8a7d70f..b099d7dfc 100644 --- a/src/laws.c +++ b/src/laws.c @@ -2798,9 +2798,6 @@ static void ageing(void) } } - /* Borders */ - age_borders(); - /* Factions */ for (f = factions; f; f = f->next) { a_age(&f->attribs, f); diff --git a/src/magic.c b/src/magic.c index 2d5fef882..871404ab4 100644 --- a/src/magic.c +++ b/src/magic.c @@ -1752,7 +1752,7 @@ static void free_spellparameters(spellparameter * param) for (i = arrlen(param); i > 0; --i) { switch (param[i - 1].typ) { case SPP_STRING: - free(param[i - 1].data.s); + free(param[i - 1].data.xs); break; default: break; diff --git a/src/magic.h b/src/magic.h index 11fa475af..949328cb3 100644 --- a/src/magic.h +++ b/src/magic.h @@ -58,7 +58,6 @@ extern "C" { struct unit *u; struct building *b; struct ship *sh; - char *s; char *xs; int i; } data; diff --git a/src/report.c b/src/report.c index e55e3e19c..744564205 100644 --- a/src/report.c +++ b/src/report.c @@ -1065,7 +1065,7 @@ static bool report_region_roads(struct stream *out, const region *r, faction *f) region *r2 = rconnect(r, d); const char *name = NULL; if (r2 && r->terrain->max_road && r2->terrain->max_road) { - if (local < r->terrain->max_road) { + if (local > 0 && local < r->terrain->max_road) { const char *temp = LOC(f->locale, mkname("border", "a_road_percent")); int percent = 100 * local / r->terrain->max_road; if (percent < 1) percent = 1; diff --git a/src/spells.c b/src/spells.c index b4dba802a..8090f8e63 100644 --- a/src/spells.c +++ b/src/spells.c @@ -30,13 +30,14 @@ #include #include #include +#include #include /* kernel includes */ #include #include -#include #include +#include #include #include "kernel/direction.h" #include @@ -105,7 +106,7 @@ static void report_spell(unit * mage, region * r, message * msg) } } -static void report_failure(unit * mage, struct order *ord) +void report_spell_failure(unit * mage, struct order *ord) { /* Fehler: "Der Zauber schlaegt fehl" */ cmistake(mage, ord, 180, MSG_MAGIC); @@ -353,7 +354,7 @@ static void magicanalyse_ship(ship * sh, unit * mage, double force) } -static int break_curse(attrib ** alist, int cast_level, double force, curse * c) +static int break_curse(attrib ** alist, int cast_level, double force, curse * c, void *curse_target) { int succ = 0; /* attrib **a = a_find(*ap, &at_curse); */ @@ -382,7 +383,7 @@ static int break_curse(attrib ** alist, int cast_level, double force, curse * c) * auf alle Verzauberungen wirken. Ansonsten pruefe, ob der Curse vom * richtigen Typ ist. */ if (!c || c == c1) { - double remain = destr_curse(c1, cast_level, force); + double remain = reduce_curse(c1, cast_level, force, curse_target); if (remain < force) { force = remain; } @@ -449,8 +450,7 @@ int report_action(region * r, unit * actor, message * msg, int flags) /* Report a spell's effect to the units in the region. */ -static void -report_effect(region * r, unit * mage, message * seen, message * unseen) +void report_spell_effect(region * r, unit * mage, message * seen, message * unseen) { int err = report_action(r, mage, seen, ACTION_RESET | ACTION_CANSEE); UNUSED_ARG(unseen); @@ -479,7 +479,7 @@ report_effect(region * r, unit * mage, message * seen, message * unseen) { message * seen = msg_message("harvest_effect", "mage", mage); message * unseen = msg_message("harvest_effect", "mage", NULL); - report_effect(r, mage, seen, unseen); + report_spell_effect(r, mage, seen, unseen); } * Meldungen an den Magier ueber Erfolg sollten, wenn sie nicht als @@ -678,6 +678,7 @@ int sp_destroy_magic(castorder * co) curse *c = NULL; char ts[80]; attrib **ap; + void *curse_target = NULL; int obj; int succ; @@ -701,6 +702,7 @@ int sp_destroy_magic(castorder * co) { /* region *tr = param->data.r; -- farcasting! */ region *tr = co_get_region(co); + curse_target = tr; ap = &tr->attribs; write_regionname(tr, caster->faction, ts, sizeof(ts)); break; @@ -708,24 +710,24 @@ int sp_destroy_magic(castorder * co) case SPP_TEMP: case SPP_UNIT: { - unit *u; - u = param->data.u; + unit *u = param->data.u; + curse_target = u; ap = &u->attribs; write_unitname(u, ts, sizeof(ts)); break; } case SPP_BUILDING: { - building *b; - b = param->data.b; + building *b = param->data.b; + curse_target = b; ap = &b->attribs; write_buildingname(b, ts, sizeof(ts)); break; } case SPP_SHIP: { - ship *sh; - sh = param->data.sh; + ship *sh = param->data.sh; + curse_target = sh; ap = &sh->attribs; write_shipname(sh, ts, sizeof(ts)); break; @@ -734,7 +736,7 @@ int sp_destroy_magic(castorder * co) return 0; } - succ = break_curse(ap, co->level, force, c); + succ = break_curse(ap, co->level, force, c, curse_target); if (succ > 0) { ADDMSG(&caster->faction->msgs, msg_message("destroy_magic_effect", @@ -928,7 +930,7 @@ int sp_magicstreet(castorder * co) { message *seen = msg_message("path_effect", "mage region", caster, r); message *unseen = msg_message("path_effect", "mage region", (unit *)NULL, r); - report_effect(r, caster, seen, unseen); + report_spell_effect(r, caster, seen, unseen); msg_release(seen); msg_release(unseen); } @@ -983,7 +985,7 @@ int sp_summonent(castorder * co) { message *seen = msg_message("ent_effect", "mage amount", caster, ents); message *unseen = msg_message("ent_effect", "mage amount", (unit *)NULL, ents); - report_effect(r, caster, seen, unseen); + report_spell_effect(r, caster, seen, unseen); msg_release(unseen); msg_release(seen); } @@ -1081,7 +1083,7 @@ int sp_maelstrom(castorder * co) if (c) { message *seen = msg_message("maelstrom_effect", "mage", caster); message *unseen = msg_message("maelstrom_effect", "mage", (unit *)NULL); - report_effect(r, caster, seen, unseen); + report_spell_effect(r, caster, seen, unseen); msg_release(seen); msg_release(unseen); } @@ -1125,7 +1127,7 @@ static int sp_mallorn(castorder * co) { message *seen = msg_message("mallorn_effect", "mage", caster); message *unseen = msg_message("mallorn_effect", "mage", (unit *)NULL); - report_effect(r, caster, seen, unseen); + report_spell_effect(r, caster, seen, unseen); msg_release(seen); msg_release(unseen); } @@ -1162,7 +1164,7 @@ int sp_blessedharvest(castorder * co) } message *seen = msg_message(effect, "mage", caster); message *unseen = msg_message(effect, "mage", (unit *)NULL); - report_effect(r, caster, seen, unseen); + report_spell_effect(r, caster, seen, unseen); msg_release(seen); msg_release(unseen); } @@ -1208,7 +1210,7 @@ static int sp_hain(castorder * co) message *seen = msg_message("growtree_effect", "mage amount", caster, trees); message *unseen = msg_message("growtree_effect", "mage amount", (unit *)NULL, trees); - report_effect(r, caster, seen, unseen); + report_spell_effect(r, caster, seen, unseen); msg_release(seen); msg_release(unseen); } @@ -1254,7 +1256,7 @@ static int sp_mallornhain(castorder * co) message *seen = msg_message("growtree_effect", "mage amount", caster, trees); message *unseen = msg_message("growtree_effect", "mage amount", (unit *)NULL, trees); - report_effect(r, caster, seen, unseen); + report_spell_effect(r, caster, seen, unseen); msg_release(seen); msg_release(unseen); } @@ -1288,7 +1290,7 @@ static void fumble_ents(const castorder * co) /* melden, 1x pro Partei */ unseen = msg_message("entrise", "region", r); - report_effect(r, mage, unseen, unseen); + report_spell_effect(r, mage, unseen, unseen); msg_release(unseen); } } @@ -1913,7 +1915,7 @@ int sp_treewalkexit(castorder * co) remaining_cap = (int)(co->force * 500); if (!params || params->typ != SPP_REGION) { - report_failure(caster, co->order); + report_spell_failure(caster, co->order); return 0; } @@ -2121,7 +2123,7 @@ static int sp_ironkeeper(castorder * co) if (r->terrain != newterrain(T_MOUNTAIN) && r->terrain != newterrain(T_GLACIER)) { - report_failure(caster, co->order); + report_spell_failure(caster, co->order); return 0; } @@ -2479,7 +2481,7 @@ int sp_fumblecurse(castorder * co) c = create_curse(caster, &target->attribs, &ct_fumble, co->force, duration, co->force / 2, 0); if (c == NULL) { - report_failure(caster, co->order); + report_spell_failure(caster, co->order); return 0; } @@ -2552,7 +2554,7 @@ static int sp_summondragon(castorder * co) if (r->terrain != newterrain(T_SWAMP) && r->terrain != newterrain(T_DESERT) && r->terrain != newterrain(T_GLACIER)) { - report_failure(caster, co->order); + report_spell_failure(caster, co->order); return 0; } @@ -2590,65 +2592,6 @@ static int sp_summondragon(castorder * co) return cast_level; } -static int sp_firewall(castorder * co) -{ - connection *b; - wall_data *fd; - region *r = co_get_region(co); - unit *caster = co_get_caster(co); - int cast_level = co->level; - double force = co->force; - spellparameter *param = co->a_params; - int dir; - region *r2; - - dir = (int) get_direction(param->data.xs, caster->faction->locale); - if (dir >= 0) { - r2 = rconnect(r, dir); - } - else { - report_failure(caster, co->order); - return 0; - } - - if (!r2 || r2 == r) { - report_failure(caster, co->order); - return 0; - } - - b = get_borders(r, r2); - while (b != NULL) { - if (b->type == &bt_firewall) - break; - b = b->next; - } - if (b == NULL) { - b = create_border(&bt_firewall, r, r2); - fd = (wall_data *)b->data.v; - fd->force = (int)(force / 2 + 0.5); - fd->mage = caster; - fd->active = false; - fd->countdown = cast_level + 1; - } - else { - fd = (wall_data *)b->data.v; - fd->force = (int)fmax(fd->force, force / 2 + 0.5); - if (fd->countdown < cast_level + 1) - fd->countdown = cast_level + 1; - } - - /* melden, 1x pro Partei */ - { - message *seen = msg_message("firewall_effect", "mage region", caster, r); - message *unseen = msg_message("firewall_effect", "mage region", (unit *)NULL, r); - report_effect(r, caster, seen, unseen); - msg_release(seen); - msg_release(unseen); - } - - return cast_level; -} - /* ------------------------------------------------------------- */ /* Name: Unheilige Kraft * Stufe: 10 @@ -2765,13 +2708,15 @@ static int change_hitpoints(unit * u, int value) return hp; } -static int dc_age(struct curse *c) +static int dc_age(struct curse *c, void *owner) /* age returns 0 if the attribute needs to be removed, !=0 otherwise */ { region *r = (region *)c->data.v; unit **up; unit *mage = c->magician; + (void) owner; + if (r == NULL || mage == NULL || mage->number == 0) { /* if the mage disappears, so does the spell. */ return AT_AGE_REMOVE; @@ -2871,7 +2816,7 @@ int sp_deathcloud(castorder * co) if (a->type == &at_curse) { curse *c = a->data.v; if (c->type == &ct_deathcloud) { - report_failure(caster, co->order); + report_spell_failure(caster, co->order); return 0; } a = a->next; @@ -3093,7 +3038,7 @@ int sp_magicboost(castorder * co) /* fehler, wenn schon ein boost */ if (is_cursed(caster->attribs, &ct_magicboost)) { - report_failure(caster, co->order); + report_spell_failure(caster, co->order); return 0; } @@ -3140,7 +3085,7 @@ static int sp_bloodsacrifice(castorder * co) int hp = (int)(co->force * 8); if (hp <= 0) { - report_failure(caster, co->order); + report_spell_failure(caster, co->order); return 0; } @@ -3161,7 +3106,7 @@ static int sp_bloodsacrifice(castorder * co) } if (aura <= 0) { - report_failure(caster, co->order); + report_spell_failure(caster, co->order); return 0; } @@ -3522,7 +3467,7 @@ static int sp_raisepeasantmob(castorder * co) if (n > rp) n = rp; if (n <= 0) { - report_failure(mage, co->order); + report_spell_failure(mage, co->order); return 0; } @@ -3752,7 +3697,7 @@ static int sp_recruit(castorder * co) const struct race *rc = f->race; if (maxp == 0) { - report_failure(mage, co->order); + report_spell_failure(mage, co->order); return 0; } /* Immer noch zuviel auf niedrigen Stufen. Deshalb die Rekrutierungskosten @@ -3807,7 +3752,7 @@ static int sp_bigrecruit(castorder * co) message *msg; if (maxp <= 0) { - report_failure(mage, co->order); + report_spell_failure(mage, co->order); return 0; } /* Fuer vergleichbare Erfolge bei unterschiedlichen Rassen die @@ -3878,7 +3823,7 @@ int sp_pump(castorder * co) return 0; } if (IS_MONSTERS(target->faction)) { - report_failure(mage, co->order); + report_spell_failure(mage, co->order); return 0; } @@ -4046,7 +3991,7 @@ static int sp_calm_monster(castorder * co) c = create_curse(caster, &target->attribs, &ct_calmmonster, force, (int)force, effect, 0); if (c == NULL) { - report_failure(caster, co->order); + report_spell_failure(caster, co->order); return 0; } @@ -4237,7 +4182,7 @@ int sp_puttorest(castorder * co) deathcounts(r, -laid_to_rest); - report_effect(r, mage, seen, unseen); + report_spell_effect(r, mage, seen, unseen); msg_release(seen); msg_release(unseen); return co->level; @@ -5804,6 +5749,7 @@ int sp_break_curse(castorder * co) int cast_level = co->level; double force = co->force; spellparameter *params = co->a_params; + void *curse_target = NULL; if (arrlen(params) < 2) { /* Das Zielobjekt wurde vergessen */ @@ -5813,7 +5759,7 @@ int sp_break_curse(castorder * co) obj = params[0].typ; - c = findcurse(atoi36(params[1].data.s)); + c = findcurse(atoi36(params[1].data.xs)); if (!c) { /* Es wurde kein Ziel gefunden */ ADDMSG(&mage->faction->msgs, msg_message("spelltargetnotfound", @@ -5824,6 +5770,7 @@ int sp_break_curse(castorder * co) attrib **ap; switch (obj) { case SPP_REGION: + curse_target = r; ap = &r->attribs; ts = regionname(r, mage->faction); break; @@ -5832,6 +5779,7 @@ int sp_break_curse(castorder * co) case SPP_UNIT: { unit *u = params[0].data.u; + curse_target = u; ap = &u->attribs; ts = itoa36(u->no); break; @@ -5839,6 +5787,7 @@ int sp_break_curse(castorder * co) case SPP_BUILDING: { building *b = params[0].data.b; + curse_target = b; ap = &b->attribs; ts = itoa36(b->no); break; @@ -5846,6 +5795,7 @@ int sp_break_curse(castorder * co) case SPP_SHIP: { ship *sh = params[0].data.sh; + curse_target = sh; ap = &sh->attribs; ts = itoa36(sh->no); break; @@ -5865,7 +5815,7 @@ int sp_break_curse(castorder * co) } /* curse aufloesen, wenn zauber staerker (force > vigour) */ - c->vigour -= force; + force = reduce_curse(c, cast_level, -force, curse_target); if (c->vigour <= 0.0) { remove_curse(ap, c); @@ -6220,6 +6170,7 @@ void register_spells(void) at_deprecate("zauber_todeswolke", dc_read_compat); ct_register(&ct_deathcloud); + ct_register(&ct_firewall); register_spelldata(); diff --git a/src/spells.h b/src/spells.h index b6f5404eb..7f940bd2d 100644 --- a/src/spells.h +++ b/src/spells.h @@ -5,6 +5,7 @@ struct curse_type; struct region; struct unit; struct message; +struct order; extern const struct curse_type ct_deathcloud; void register_spells(void); @@ -53,3 +54,6 @@ int sp_pump(struct castorder *co); int sp_readmind(struct castorder *co); int sp_auraleak(struct castorder *co); int sp_movecastle(struct castorder *co); + +void report_spell_effect(struct region *r, struct unit *mage, struct message *seen, struct message *unseen); +void report_spell_failure(struct unit *mage, struct order *ord); diff --git a/src/spells.test.c b/src/spells.test.c index 173474de8..6785ec69f 100644 --- a/src/spells.test.c +++ b/src/spells.test.c @@ -7,6 +7,7 @@ #include #include #include +#include "kernel/connection.h" #include "kernel/direction.h" #include "kernel/event.h" #include @@ -27,10 +28,12 @@ #include "util/rand.h" #include "util/variant.h" // for variant +#include #include #include #include #include +#include #include #include #include @@ -1901,6 +1904,87 @@ static void test_analysemagic_region(CuTest *tc) test_teardown(); } +static void test_firewall_spell(CuTest *tc) +{ + unit *u; + castorder co; + spellparameter param, *args = NULL; + region *r2; + connection *wall; + + test_setup(); + u = test_create_unit(test_create_faction(), test_create_plain(0, 0)); + r2 = test_create_plain(1, 0); + u->faction->magiegebiet = M_DRAIG; + + param.flag = TARGET_OK; + param.typ = SPP_STRING; + param.data.xs = str_strdup(locale_string(u->faction->locale, directions[D_EAST], true)); + arrput(args, param); + + test_create_castorder(&co, u, 3, 4., 0, args); + CuAssertIntEquals(tc, co.level, sp_firewall(&co)); + CuAssertPtrNotNull(tc, wall = get_borders(u->region, r2)); + CuAssertPtrEquals(tc, &bt_firewall, wall->type); + + test_teardown(); +} + +static void test_create_firewall(CuTest *tc) +{ + unit *u; + region *r2, *r1; + connection *wall; + wall_data *fd; + curse *c; + double force; + + test_setup(); + u = test_create_unit(test_create_faction(), r1 = test_create_plain(0, 0)); + r2 = test_create_plain(1, 0); + create_firewall(u, r1, D_EAST, force = 3.0, 2); + CuAssertPtrNotNull(tc, wall = get_borders(u->region, r2)); + CuAssertPtrEquals(tc, &bt_firewall, wall->type); + CuAssertPtrNotNull(tc, fd = (wall_data *)wall->data.v); + CuAssertTrue(tc, ! fd->active); + CuAssertDblEquals(tc, 3.0, fd->force, 0.01); + CuAssertIntEquals(tc, 4, fd->countdown); + + CuAssertPtrNotNull(tc, c = get_curse(r1->attribs, &ct_firewall)); + CuAssertIntEquals(tc, 3, c->duration); + CuAssertDblEquals(tc, force, c->vigour, 0.01); + CuAssertPtrEquals(tc, u, c->magician); + CuAssertIntEquals(tc, D_EAST, c->data.sa[0]); + + CuAssertPtrNotNull(tc, c = get_curse(r2->attribs, &ct_firewall)); + CuAssertIntEquals(tc, 3, c->duration); + CuAssertDblEquals(tc, force, c->vigour, 0.01); + CuAssertPtrEquals(tc, u, c->magician); + CuAssertIntEquals(tc, D_WEST, c->data.sa[0]); + + test_teardown(); +} + +static void test_destroy_firewall(CuTest *tc) +{ + unit *u; + region *r2, *r1; + curse *c; + double force; + + test_setup(); + u = test_create_unit(test_create_faction(), r1 = test_create_plain(0, 0)); + r2 = test_create_plain(1, 0); + create_firewall(u, r1, D_EAST, force = 3.0, 2); + + CuAssertPtrNotNull(tc, c = get_curse(r1->attribs, &ct_firewall)); + CuAssertDblEquals(tc, 0.0, reduce_curse(c, 4, c->vigour, r1), 0.001); + + CuAssertPtrEquals(tc, NULL, get_borders(u->region, r2)); + + test_teardown(); +} + CuSuite *get_spells_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -1951,6 +2035,9 @@ CuSuite *get_spells_suite(void) SUITE_ADD_TEST(suite, test_analysemagic_ship); SUITE_ADD_TEST(suite, test_analysemagic_building); SUITE_ADD_TEST(suite, test_analysemagic_region); + SUITE_ADD_TEST(suite, test_firewall_spell); + SUITE_ADD_TEST(suite, test_create_firewall); + SUITE_ADD_TEST(suite, test_destroy_firewall); return suite; } diff --git a/src/spells/CMakeLists.txt b/src/spells/CMakeLists.txt index 083808ce3..a506fa17f 100644 --- a/src/spells/CMakeLists.txt +++ b/src/spells/CMakeLists.txt @@ -7,6 +7,7 @@ regioncurse.c shipcurse.c unitcurse.c flyingship.c +firewall.c charming.c ) diff --git a/src/spells/borders.c b/src/spells/borders.c index 74650629a..93062dbbf 100644 --- a/src/spells/borders.c +++ b/src/spells/borders.c @@ -57,6 +57,7 @@ static void wall_init(connection * b) if (!fd) abort(); fd->countdown = -1; /* infinite */ b->data.v = fd; + fd->active = false; } static void wall_destroy(connection * b) @@ -69,7 +70,9 @@ static void wall_read(connection * b, gamedata * data) static wall_data dummy; wall_data *fd = b->data.v ? (wall_data *)b->data.v : &dummy; - read_unit_reference(data, &fd->mage, NULL); + if (data->version < WALL_DATA_VERSION) { + read_unit_reference(data, NULL, NULL); + } READ_INT(data->store, &fd->force); READ_INT(data->store, &fd->countdown); fd->active = true; @@ -78,18 +81,10 @@ static void wall_read(connection * b, gamedata * data) static void wall_write(const connection * b, storage * store) { wall_data *fd = (wall_data *)b->data.v; - write_unit_reference(fd->mage, store); WRITE_INT(store, fd->force); WRITE_INT(store, fd->countdown); } -static int wall_age(connection * b) -{ - wall_data *fd = (wall_data *)b->data.v; - --fd->countdown; - return (fd->countdown > 0) ? AT_AGE_KEEP : AT_AGE_REMOVE; -} - static region *wall_move(const connection * b, struct unit *u, struct region *from, struct region *to, bool routing) { @@ -143,8 +138,7 @@ border_type bt_firewall = { b_finvisible, /* fvisible */ b_uinvisible, /* uvisible */ NULL, - wall_move, - wall_age + wall_move }; void convert_firewall_timeouts(connection * b, attrib * a) diff --git a/src/spells/borders.h b/src/spells/borders.h index 37c9a2665..2cb51445d 100644 --- a/src/spells/borders.h +++ b/src/spells/borders.h @@ -12,10 +12,8 @@ void register_borders(void); **/ extern struct border_type bt_chaosgate; extern struct border_type bt_firewall; -extern const struct curse_type ct_firewall; typedef struct wall_data { - struct unit *mage; int force; bool active; int countdown; diff --git a/src/spells/charming.c b/src/spells/charming.c index ac884ce80..3f58e9505 100644 --- a/src/spells/charming.c +++ b/src/spells/charming.c @@ -88,9 +88,11 @@ static int slave_write(struct storage *store, const struct curse *c, const void return 0; } -static int slave_age(struct curse *c) +static int slave_age(struct curse *c, void *owner) { slave_data *sd = (slave_data *)c->data.v; + (void) owner; + --c->duration; if (sd) { if (c->duration == 0) { sd->self->faction = sd->faction; diff --git a/src/spells/firewall.c b/src/spells/firewall.c new file mode 100644 index 000000000..9430bcdac --- /dev/null +++ b/src/spells/firewall.c @@ -0,0 +1,174 @@ +#include "firewall.h" + +#include "spells.h" +#include "borders.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* libc includes */ +#include +#include + +static connection *find_firewall(const region *r1, const region *r2) +{ + connection *b = get_borders(r1, r2); + for (; b; b = b->next) { + if (b->type == &bt_firewall) { + return b; + } + } + return NULL; +} + +static struct message *firewall_info(const void *obj, objtype_t typ, + const struct curse * c, int self) +{ + (void)obj; + (void)typ; + (void)self; + return msg_message("curseinfo::firewall", "direction id", c->data.sa[0], c->no); +} + +static void firewall_change(curse *c, double delta, void *owner) +{ + assert(c); + c->vigour += delta; + if (c->vigour <= 0) { + direction_t dir = c->data.sa[0]; + region *r = (region *)owner; + region *r2 = rconnect(r, dir); + connection *b = find_firewall(r, r2); + if (b) { + erase_border(b); + } + } +} + +static int firewall_read(gamedata *data, curse *c, void *owner) { + int i; + READ_INT(data->store, &i); /* direction */ + c->data.sa[0] = (direction_t)i; + return 0; +} + +static int firewall_write(storage *store, const curse *c, const void *owner) { + WRITE_INT(store, c->data.sa[0]); /* direction */ + return 0; +} + +static int firewall_age(struct curse *c, void *owner) +{ + direction_t dir = c->data.sa[0]; + region *r = (region *)owner; + region *r2 = rconnect(r, dir); + connection *b = find_firewall(r, r2); + if (b) { + wall_data *wd = (wall_data *)b->data.v; + if (0 == wd->countdown || c->vigour <= 0.0) { + curse *c2 = get_curse(r2->attribs, &ct_firewall); + if (c2) { + remove_curse(&r2->attribs, c2); + } + erase_border(b); + return AT_AGE_REMOVE; + } + else { + wd->active = true; + --wd->countdown; + return AT_AGE_KEEP; + } + } + + /* if the firewall is gone, we no longer need the curse */ + return AT_AGE_REMOVE; +} + +const struct curse_type ct_firewall = { + "firewall", CURSETYP_REGION, 0, NO_MERGE, + firewall_info, + firewall_change, + firewall_read, + firewall_write, + NULL, + firewall_age, +}; + +void create_firewall(struct unit *mage, struct region *r, enum direction_t d, double force, int duration) +{ + region *r2 = rconnect(r, d); + connection *b = get_borders(r, r2); + wall_data *fd; + curse *c; + while (b != NULL) { + if (b->type == &bt_firewall) + break; + b = b->next; + } + if (b == NULL) { + b = create_border(&bt_firewall, r, r2); + fd = (wall_data *)b->data.v; + fd->force = (int)force; + fd->active = false; + fd->countdown = duration * 2; + } + else { + fd = (wall_data *)b->data.v; + fd->force = (int)fmax(fd->force, force); + if (fd->countdown < duration) + fd->countdown = duration; + } + c = create_curse(mage, &r->attribs, &ct_firewall, force, duration + 1, 0.0, 0); + c->data.sa[0] = d; /* direction of r2 */ + c = create_curse(mage, &r2->attribs, &ct_firewall, force, duration + 1, 0.0, 0); + c->data.sa[0] = d_reverse(d); /* direction of r */ +} + +int sp_firewall(castorder *co) +{ + region *r = co_get_region(co); + unit *caster = co_get_caster(co); + int cast_level = co->level; + double force = co->force; + spellparameter *param = co->a_params; + int dir; + region *r2; + + dir = (int)get_direction(param->data.xs, caster->faction->locale); + if (dir >= 0) { + r2 = rconnect(r, dir); + } + else { + report_spell_failure(caster, co->order); + return 0; + } + + if (!r2 || r2 == r) { + report_spell_failure(caster, co->order); + return 0; + } + + create_firewall(caster, r, dir, force / 2 + 0.5, cast_level); + + /* melden, 1x pro Partei */ + { + message *seen = msg_message("firewall_effect", "mage region", caster, r); + message *unseen = msg_message("firewall_effect", "mage region", (unit *)NULL, r); + report_spell_effect(r, caster, seen, unseen); + msg_release(seen); + msg_release(unseen); + } + + return cast_level; +} + diff --git a/src/spells/firewall.h b/src/spells/firewall.h new file mode 100644 index 000000000..8d0f15302 --- /dev/null +++ b/src/spells/firewall.h @@ -0,0 +1,11 @@ +#pragma once + +struct castorder; +struct unit; +struct region; +enum direction_t; + +void create_firewall(struct unit *mage, struct region *r, enum direction_t d, double force, int duration); +int sp_firewall(struct castorder *co); + +extern const struct curse_type ct_firewall;