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;