From 0884fb1f1bb4f6691b8621fc9cb5bb7e865ae763 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Wed, 7 Feb 2018 18:05:14 +0100
Subject: [PATCH 01/40] CID 182685 check for utf8 encoding errors.

---
 src/summary.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/src/summary.c b/src/summary.c
index 79ffd1b6d..ee047bc8f 100644
--- a/src/summary.c
+++ b/src/summary.c
@@ -178,9 +178,14 @@ static int count_umlaut(const char *s)
         ucs4_t ucs = *cp;
         if (ucs & 0x80) {
             size_t size;
-            ++result;
-            unicode_utf8_to_ucs4(&ucs, cp, &size);
+            int err;
+            err = unicode_utf8_to_ucs4(&ucs, cp, &size);
+            if (err != 0) {
+                log_error("illegal utf8 encoding %s at %s", s, cp);
+                return result;
+            }
             cp += size;
+            ++result;
         }
     }
     return result;

From 9ee84445ebbfaebe1ef1cae29aef23d97ee82539 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Wed, 7 Feb 2018 18:39:20 +0100
Subject: [PATCH 02/40] stop using MIN and MAX macros

---
 src/kernel/building.c     | 31 +++++++++++++-------
 src/spells/combatspells.c | 61 ++++++++++++++++++++++-----------------
 2 files changed, 56 insertions(+), 36 deletions(-)

diff --git a/src/kernel/building.c b/src/kernel/building.c
index 35efa8579..8c5fbeb14 100644
--- a/src/kernel/building.c
+++ b/src/kernel/building.c
@@ -16,7 +16,9 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 **/
 
+#ifdef _MSC_VER
 #include <platform.h>
+#endif
 
 #include <kernel/config.h>
 #include "building.h"
@@ -150,10 +152,11 @@ building_type *bt_get_or_create(const char *name)
 int buildingcapacity(const building * b)
 {
     if (b->type->capacity >= 0) {
-        if (b->type->maxcapacity >= 0) {
-            return MIN(b->type->maxcapacity, b->size * b->type->capacity);
+        int cap = b->size * b->type->capacity;
+        if (b->type->maxcapacity > 0 && b->type->maxcapacity < cap) {
+            cap = b->type->maxcapacity;
         }
-        return b->size * b->type->capacity;
+        return cap;
     }
     if (building_finished(b)) {
         if (b->type->maxcapacity >= 0) {
@@ -313,9 +316,15 @@ int building_protection(const building_type * btype, int stage)
 {
     assert(btype->flags & BTF_FORTIFICATION);
     if (btype->maxsize < 0) {
-        return castle_bonus[MIN(stage, 5)];
+        if (stage > 5) {
+            stage = 5;
+        }
+        return castle_bonus[stage];
     }
-    return watch_bonus[MIN(stage, 2)];
+    if (stage > 2) {
+        stage = 2;
+    }
+    return watch_bonus[stage];
 }
 
 void write_building_reference(const struct building *b, struct storage *store)
@@ -682,7 +691,7 @@ default_wage(const region * r, const faction * f, const race * rc, int in_turn)
 {
     building *b = largestbuilding(r, cmp_wage, false);
     int esize = 0;
-    double wage;
+    int wage;
 
     if (b != NULL) {
         /* TODO: this reveals imaginary castles */
@@ -715,25 +724,27 @@ default_wage(const region * r, const faction * f, const race * rc, int in_turn)
     if (r->attribs) {
         attrib *a;
         curse *c;
+        variant vm = frac_make(wage, 1);
 
         /* Godcurse: Income -10 */
         c = get_curse(r->attribs, &ct_godcursezone);
         if (c && curse_active(c)) {
-            wage = MAX(0, wage - 10);
+            wage = (wage < 10) ? 0 : (wage - 10);
         }
 
         /* Bei einer D�rre verdient man nur noch ein Viertel  */
         c = get_curse(r->attribs, &ct_drought);
         if (c && curse_active(c)) {
-            wage /= curse_geteffect(c);
+            vm = frac_mul(vm, frac_make(1, curse_geteffect_int(c)));
         }
 
         a = a_find(r->attribs, &at_reduceproduction);
         if (a) {
-            wage = (wage * a->data.sa[0]) / 100;
+            vm = frac_mul(vm, frac_make(a->data.sa[0], 100));
         }
+        wage = vm.sa[0] / vm.sa[1];
     }
-    return (int)wage;
+    return wage;
 }
 
 static int
diff --git a/src/spells/combatspells.c b/src/spells/combatspells.c
index ce737f570..adaa832d9 100644
--- a/src/spells/combatspells.c
+++ b/src/spells/combatspells.c
@@ -9,7 +9,9 @@
  This program may not be used, modified or distributed
  without prior permission by the authors of Eressea.
  */
+#ifdef _MSC_VER
 #include <platform.h>
+#endif
 #include "combatspells.h"
 
 #include <spells/buildingcurse.h>
@@ -45,6 +47,7 @@
 
 /* libc includes */
 #include <assert.h>
+#include <math.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -131,8 +134,7 @@ int damage_spell(struct castorder * co, int dmg, int strength)
 
     enemies = count_enemies(b, fi, FIGHT_ROW, BEHIND_ROW - 1, SELECT_ADVANCE);
     if (enemies == 0) {
-        message *m =
-            msg_message("spell_out_of_range", "mage spell", fi->unit, sp);
+        m = msg_message("spell_out_of_range", "mage spell", fi->unit, sp);
         message_all(b, m);
         msg_release(m);
         return 0;
@@ -171,8 +173,7 @@ int sp_petrify(struct castorder * co)
 
     enemies = count_enemies(b, fi, FIGHT_ROW, BEHIND_ROW, SELECT_ADVANCE);
     if (!enemies) {
-        message *m =
-            msg_message("spell_out_of_range", "mage spell", fi->unit, sp);
+        m = msg_message("spell_out_of_range", "mage spell", fi->unit, sp);
         message_all(b, m);
         msg_release(m);
         return 0;
@@ -218,8 +219,7 @@ int sp_stun(struct castorder * co)
 
     enemies = count_enemies(b, fi, FIGHT_ROW, BEHIND_ROW, SELECT_ADVANCE);
     if (!enemies) {
-        message *m =
-            msg_message("spell_out_of_range", "mage spell", fi->unit, sp);
+        m = msg_message("spell_out_of_range", "mage spell", fi->unit, sp);
         message_all(b, m);
         msg_release(m);
         return 0;
@@ -298,7 +298,8 @@ int sp_combatrosthauch(struct castorder * co)
 
         for (w = 0; df->weapons[w].type != NULL; ++w) {
             weapon *wp = df->weapons;
-            int n = MIN(force, wp->used);
+            int n = force;
+            if (n < wp->used) n = wp->used;
             if (n) {
                 requirement *mat = wp->type->itype->construction->materials;
                 bool iron = false;
@@ -682,8 +683,7 @@ int sp_immolation(struct castorder * co)
     force = 99999;
 
     if (!count_enemies(b, fi, FIGHT_ROW, AVOID_ROW, SELECT_ADVANCE | SELECT_FIND)) {
-        message *m =
-            msg_message("spell_out_of_range", "mage spell", fi->unit, sp);
+        m = msg_message("spell_out_of_range", "mage spell", fi->unit, sp);
         message_all(b, m);
         msg_release(m);
         return 0;
@@ -791,7 +791,7 @@ int sp_shadowknights(struct castorder * co)
     region *r = b->region;
     unit *mage = fi->unit;
     attrib *a;
-    int force = MAX(1, (int)get_force(power, 3));
+    int force = (int)fmax(1, get_force(power, 3));
     message *msg;
 
     u =
@@ -890,7 +890,6 @@ int sp_chaosrow(struct castorder * co)
             continue;
         if (power <= 0.0)
             break;
-        /* force sollte wegen des MAX(0,x) nicht unter 0 fallen k�nnen */
 
         if (is_magic_resistant(mage, df->unit, 0))
             continue;
@@ -925,7 +924,7 @@ int sp_chaosrow(struct castorder * co)
             }
             k += df->alive;
         }
-        power = MAX(0, power - n);
+        power = fmax(0, power - n);
     }
     selist_free(fgs);
 
@@ -1020,7 +1019,8 @@ int sp_hero(struct castorder * co)
     message *m;
 
     df_bonus = (int)(power / 5);
-    force = MAX(1, lovar(get_force(power, 4)));
+    force = lovar(get_force(power, 4));
+    if (force < 1) force = 1;
 
     allies =
         count_allies(fi->side, FIGHT_ROW, BEHIND_ROW, SELECT_ADVANCE, ALLY_ANY);
@@ -1065,7 +1065,8 @@ int sp_berserk(struct castorder * co)
     int targets = 0;
     message *m;
 
-    at_bonus = MAX(1, level / 3);
+    at_bonus = level / 3;
+    if (at_bonus < 1) at_bonus = 1;
     df_malus = 2;
     force = (int)get_force(power, 2);
 
@@ -1114,14 +1115,14 @@ int sp_frighten(struct castorder * co)
     int targets = 0;
     message *m;
 
-    at_malus = MAX(1, level - 4);
+    at_malus = level - 4;
+    if (at_malus < 1) at_malus = 1;
     df_malus = 2;
     force = (int)get_force(power, 2);
 
     enemies = count_enemies(b, fi, FIGHT_ROW, BEHIND_ROW - 1, SELECT_ADVANCE);
     if (!enemies) {
-        message *m =
-            msg_message("spell_out_of_range", "mage spell", fi->unit, sp);
+        m = msg_message("spell_out_of_range", "mage spell", fi->unit, sp);
         message_all(b, m);
         msg_release(m);
         return 0;
@@ -1170,8 +1171,7 @@ int sp_tiredsoldiers(struct castorder * co)
 
     if (!count_enemies(b, fi, FIGHT_ROW, BEHIND_ROW,
         SELECT_ADVANCE | SELECT_FIND)) {
-        message *m =
-            msg_message("spell_out_of_range", "mage spell", fi->unit, sp);
+        m = msg_message("spell_out_of_range", "mage spell", fi->unit, sp);
         message_all(b, m);
         msg_release(m);
         return 0;
@@ -1367,7 +1367,8 @@ int sp_fumbleshield(struct castorder * co)
 
     /* der erste Zauber schl�gt mit 100% fehl  */
     duration = 100;
-    effect = MAX(1, 25 - level);
+    effect = 25 - level;
+    if (effect < 1) effect = 1;
 
     do_meffect(fi, SHIELD_BLOCK, effect, duration);
     return level;
@@ -1403,7 +1404,7 @@ int sp_reanimate(struct castorder * co)
     unit *mage = fi->unit;
     int healable, j = 0;
     double c = 0.50 + 0.02 * power;
-    double k = EFFECT_HEALING_SPELL * power;
+    int k = (int)(EFFECT_HEALING_SPELL * power);
     bool use_item = has_ao_healing(mage);
     message *msg;
 
@@ -1413,7 +1414,9 @@ int sp_reanimate(struct castorder * co)
     }
 
     healable = count_healable(b, fi);
-    healable = (int)MIN(k, healable);
+    if (healable > k) {
+        healable = k;
+    }
     while (healable--) {
         fighter *tf = select_corpse(b, fi);
         if (tf != NULL && tf->side->casualties > 0
@@ -1466,7 +1469,7 @@ int sp_keeploot(struct castorder * co)
     message_all(b, m);
     msg_release(m);
 
-    b->keeploot = (int)MAX(25, b->keeploot + 5 * power);
+    b->keeploot = (int)fmax(25, b->keeploot + 5 * power);
 
     return level;
 }
@@ -1497,10 +1500,14 @@ static int heal_fighters(selist * fgs, int *power, bool heal_monsters)
                     ++wound;
 
                 if (wound > 0 && wound < hp) {
-                    int heal = MIN(healhp, wound);
+                    int heal = healhp;
+                    if (heal > wound) {
+                        heal = wound;
+                    }
                     assert(heal >= 0);
                     df->person[n].hp += heal;
-                    healhp = MAX(0, healhp - heal);
+                    healhp -= heal;
+                    if (healhp < 0) healhp = 0;
                     ++healed;
                     if (healhp <= 0)
                         break;
@@ -1653,7 +1660,9 @@ int sp_undeadhero(struct castorder * co)
     }
     selist_free(fgs);
 
-    level = MIN(level, undead);
+    if (level > undead) {
+        level = undead;
+    }
     if (undead == 0) {
         msg =
             msg_message("summonundead_effect_0", "mage region", mage, mage->region);

From 5a3978566ae1d6b48fae65a90540feec021ebe65 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Wed, 7 Feb 2018 18:46:31 +0100
Subject: [PATCH 03/40] adamantium and laen weapons are magical.

---
 res/adamantium.xml             | 2 +-
 res/core/weapons/laensword.xml | 2 +-
 res/e3a/weapons.xml            | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/res/adamantium.xml b/res/adamantium.xml
index 563299e51..2e2311fcd 100644
--- a/res/adamantium.xml
+++ b/res/adamantium.xml
@@ -16,7 +16,7 @@
         <requirement type="adamantium" quantity="1"/>
         <requirement type="log" quantity="1"/>
       </construction>
-      <weapon cut="true" skill="melee" offmod="2" defmod="-2" magres="0.30">
+      <weapon cut="true" magical="true" skill="melee" offmod="2" defmod="-2" magres="0.30">
         <damage type="rider" value="3d4+15"/>
         <damage type="footman" value="3d4+15"/>
       </weapon>
diff --git a/res/core/weapons/laensword.xml b/res/core/weapons/laensword.xml
index 325d25d10..2746a6384 100644
--- a/res/core/weapons/laensword.xml
+++ b/res/core/weapons/laensword.xml
@@ -6,7 +6,7 @@
     <construction skill="weaponsmithing" minskill="8">
       <requirement type="laen" quantity="1"/>
     </construction>
-    <weapon cut="true" skill="melee" offmod="1" defmod="1" magres="0.30">
+    <weapon cut="true" magical="true" skill="melee" offmod="1" defmod="1" magres="0.30">
       <damage type="rider" value="3d6+10"/>
       <damage type="footman" value="3d6+10"/>
     </weapon>
diff --git a/res/e3a/weapons.xml b/res/e3a/weapons.xml
index 5b57cbed0..3a75fe350 100644
--- a/res/e3a/weapons.xml
+++ b/res/e3a/weapons.xml
@@ -86,7 +86,7 @@
     <construction skill="weaponsmithing" minskill="8">
       <requirement type="laen" quantity="1"/>
     </construction>
-    <weapon cut="true" skill="melee" offmod="1" defmod="1" magres="0.30">
+    <weapon cut="true" magical="true" skill="melee" offmod="1" defmod="1" magres="0.30">
       <damage type="rider" value="2d9+4"/>
       <damage type="footman" value="2d9+4"/>
     </weapon>

From 4f63cf12b2ca11523f2ae11a4802438c3c06c8d6 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Wed, 7 Feb 2018 19:35:24 +0100
Subject: [PATCH 04/40] firesword, magical damage

---
 res/core/weapons/firesword.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/res/core/weapons/firesword.xml b/res/core/weapons/firesword.xml
index dc3b47af5..0285db7ea 100644
--- a/res/core/weapons/firesword.xml
+++ b/res/core/weapons/firesword.xml
@@ -3,7 +3,7 @@
 <resources>
 <resource name="firesword">
   <item weight="100">
-    <weapon magres="0.3" cut="true" skill="melee" offmod="1" defmod="1">
+    <weapon magres="0.3" cut="true" magical="true" skill="melee" offmod="1" defmod="1">
       <function name="attack" value="attack_firesword"/>
       <damage type="rider" value="3d6+10"/>
       <damage type="footman" value="3d6+10"/>

From 87081a37bc531753f1a127abffa6961abba94f3d Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Thu, 8 Feb 2018 08:33:27 +0100
Subject: [PATCH 05/40] CID 182717 call fclose, always

---
 src/jsonconf.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/jsonconf.c b/src/jsonconf.c
index cd9d991aa..dcab0e9c6 100644
--- a/src/jsonconf.c
+++ b/src/jsonconf.c
@@ -932,6 +932,7 @@ static int include_json(const char *uri) {
     FILE *F;
     char name[PATH_MAX];
     const char *filename = uri_to_file(uri, name, sizeof(name));
+    int result = -1;
 
     F = fopen(filename, "r");
     if (F) {
@@ -952,15 +953,16 @@ static int include_json(const char *uri) {
             if (config) {
                 json_config(config);
                 cJSON_Delete(config);
+                result = 0;
             }
             else {
                 log_error("could not parse JSON from %s", uri);
-                return -1;
+                result = -1;
             }
         }
         fclose(F);
     }
-    return 0;
+    return result;
 }
 
 static int include_xml(const char *uri) {

From ec4801752041b8cdbff47ce937dfd7cab1a64a59 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Thu, 8 Feb 2018 18:33:58 +0100
Subject: [PATCH 06/40] move spell reporting attributes to a separate module.

---
 src/CMakeLists.txt            |   2 +-
 src/attributes/CMakeLists.txt |   1 +
 src/attributes/attributes.c   |   3 +-
 src/attributes/seenspell.c    | 126 ++++++++++++++++++++++++++++++++++
 src/attributes/seenspell.h    |  13 ++++
 src/creport.c                 |   1 +
 src/laws.c                    |   1 +
 src/magic.c                   |  88 ------------------------
 src/magic.h                   |   2 -
 src/report.c                  |   1 +
 10 files changed, 146 insertions(+), 92 deletions(-)
 create mode 100644 src/attributes/seenspell.c
 create mode 100644 src/attributes/seenspell.h

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 8a17a45e9..a74d1d732 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -93,6 +93,7 @@ set (ERESSEA_SRC
   battle.c
   alchemy.c
   academy.c
+  chaos.c
   upkeep.c
   names.c
   lighthouse.c
@@ -121,7 +122,6 @@ set (ERESSEA_SRC
   randenc.c
   renumber.c
   volcano.c
-  chaos.c
   spy.c
   study.c
   summary.c
diff --git a/src/attributes/CMakeLists.txt b/src/attributes/CMakeLists.txt
index fb077cfe9..c76f2458a 100644
--- a/src/attributes/CMakeLists.txt
+++ b/src/attributes/CMakeLists.txt
@@ -20,6 +20,7 @@ racename.c
 raceprefix.c
 reduceproduction.c
 stealth.c
+seenspell.c
 targetregion.c
 )
 FOREACH(_FILE ${_FILES})
diff --git a/src/attributes/attributes.c b/src/attributes/attributes.c
index a5911200c..cb6e335b5 100644
--- a/src/attributes/attributes.c
+++ b/src/attributes/attributes.c
@@ -22,6 +22,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 #include "laws.h"
 #include "move.h"
+#include "magic.h"
 
 /* attributes includes */
 #include "follow.h"
@@ -29,7 +30,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include "iceberg.h"
 #include "key.h"
 #include "stealth.h"
-#include "magic.h"
 #include "moved.h"
 #include "movement.h"
 #include "dict.h"
@@ -38,6 +38,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include "racename.h"
 #include "raceprefix.h"
 #include "reduceproduction.h"
+#include "seenspell.h"
 #include "targetregion.h"
 
 /* kernel includes */
diff --git a/src/attributes/seenspell.c b/src/attributes/seenspell.c
new file mode 100644
index 000000000..99ac84873
--- /dev/null
+++ b/src/attributes/seenspell.c
@@ -0,0 +1,126 @@
+/*
+Copyright (c) 1998-2014,
+Enno Rehling <enno@eressea.de>
+Katja Zedel <katze@felidae.kn-bremen.de
+Christian Schlittchen <corwin@amber.kn-bremen.de>
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+**/
+
+#ifdef _MSC_VER
+#include <platform.h>
+#endif
+#include <kernel/faction.h>
+#include <kernel/spell.h>
+#include <kernel/spellbook.h>
+#include <util/attrib.h>
+#include <util/gamedata.h>
+#include <util/log.h>
+#include <util/macros.h>
+
+#include "seenspell.h"
+
+#include <selist.h>
+#include <storage.h>
+
+#include <stdlib.h>
+
+/* ------------------------------------------------------------- */
+/* Ausgabe der Spruchbeschreibungen
+* Anzeige des Spruchs nur, wenn die Stufe des besten Magiers vorher
+* kleiner war (u->faction->seenspells). Ansonsten muss nur geprüft
+* werden, ob dieser Magier den Spruch schon kennt, und andernfalls der
+* Spruch zu seiner List-of-known-spells hinzugefügt werden.
+*/
+
+static int read_seenspell(attrib * a, void *owner, struct gamedata *data)
+{
+    storage *store = data->store;
+    spell *sp = 0;
+    char token[32];
+
+    UNUSED_ARG(owner);
+    READ_TOK(store, token, sizeof(token));
+    if (data->version < UNIQUE_SPELLS_VERSION) {
+        READ_INT(store, 0); /* ignore mtype */
+    }
+    sp = find_spell(token);
+    if (!sp) {
+        log_info("read_seenspell: could not find spell '%s'\n", token);
+        return AT_READ_FAIL;
+    }
+    a->data.v = sp;
+    return AT_READ_OK;
+}
+
+static void
+write_seenspell(const attrib * a, const void *owner, struct storage *store)
+{
+    const spell *sp = (const spell *)a->data.v;
+    UNUSED_ARG(owner);
+    WRITE_TOK(store, sp->sname);
+}
+
+attrib_type at_seenspell = {
+    "seenspell", NULL, NULL, NULL, write_seenspell, read_seenspell
+};
+
+static bool already_seen(const faction * f, const spell * sp)
+{
+    attrib *a;
+
+    for (a = a_find(f->attribs, &at_seenspell); a && a->type == &at_seenspell;
+        a = a->next) {
+        if (a->data.v == sp)
+            return true;
+    }
+    return false;
+}
+
+static void a_init_reportspell(struct attrib *a) {
+    a->data.v = calloc(1, sizeof(spellbook_entry));
+}
+
+static void a_finalize_reportspell(struct attrib *a) {
+    free(a->data.v);
+}
+
+attrib_type at_reportspell = {
+    "reportspell",
+    a_init_reportspell,
+    a_finalize_reportspell,
+    0, NO_WRITE, NO_READ
+};
+
+void show_new_spells(faction * f, int level, const spellbook *book)
+{
+    if (book) {
+        selist *ql = book->spells;
+        int qi;
+
+        for (qi = 0; ql; selist_advance(&ql, &qi, 1)) {
+            spellbook_entry *sbe = (spellbook_entry *)selist_get(ql, qi);
+            if (sbe->level <= level) {
+                if (!already_seen(f, sbe->sp)) {
+                    attrib * a = a_new(&at_reportspell);
+                    spellbook_entry * entry = (spellbook_entry *)a->data.v;
+                    entry->level = sbe->level;
+                    entry->sp = sbe->sp;
+                    a_add(&f->attribs, a);
+                    a_add(&f->attribs, a_new(&at_seenspell))->data.v = sbe->sp;
+                }
+            }
+        }
+    }
+}
+
diff --git a/src/attributes/seenspell.h b/src/attributes/seenspell.h
new file mode 100644
index 000000000..488430575
--- /dev/null
+++ b/src/attributes/seenspell.h
@@ -0,0 +1,13 @@
+#ifndef H_SEENSPELL
+#define H_SEENSPELL
+
+struct attrib_type;
+struct spellbook;
+struct faction;
+
+void show_new_spells(struct faction * f, int level, const struct spellbook *book);
+
+extern struct attrib_type at_reportspell;
+extern struct attrib_type at_seenspell;
+
+#endif
diff --git a/src/creport.c b/src/creport.c
index fa17c426d..04f6c6bc4 100644
--- a/src/creport.c
+++ b/src/creport.c
@@ -30,6 +30,7 @@ without prior permission by the authors of Eressea.
 #include <attributes/otherfaction.h>
 #include <attributes/racename.h>
 #include <attributes/raceprefix.h>
+#include <attributes/seenspell.h>
 #include <attributes/stealth.h>
 
 /* gamecode includes */
diff --git a/src/laws.c b/src/laws.c
index 85b820323..9e2eb9471 100644
--- a/src/laws.c
+++ b/src/laws.c
@@ -48,6 +48,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 /* attributes includes */
 #include <attributes/racename.h>
 #include <attributes/raceprefix.h>
+#include <attributes/seenspell.h>
 #include <attributes/stealth.h>
 
 #include <spells/buildingcurse.h>
diff --git a/src/magic.c b/src/magic.c
index 6855c307f..4e9c87b4e 100644
--- a/src/magic.c
+++ b/src/magic.c
@@ -101,20 +101,6 @@ const char *magic_school[MAXMAGIETYP] = {
     "common"
 };
 
-static void a_init_reportspell(struct attrib *a) {
-    a->data.v = calloc(1, sizeof(spellbook_entry));
-}
-
-static void a_finalize_reportspell(struct attrib *a) {
-    free(a->data.v);
-}
-
-attrib_type at_reportspell = {
-    "reportspell",
-    a_init_reportspell,
-    a_finalize_reportspell,
-    0, NO_WRITE, NO_READ
-};
 /**
  ** at_icastle
  ** TODO: separate castle-appearance from illusion-effects
@@ -354,82 +340,8 @@ sc_mage *get_mage_depr(const unit * u)
     return NULL;
 }
 
-/* ------------------------------------------------------------- */
-/* Ausgabe der Spruchbeschreibungen
-* Anzeige des Spruchs nur, wenn die Stufe des besten Magiers vorher
-* kleiner war (u->faction->seenspells). Ansonsten muss nur geprüft
-* werden, ob dieser Magier den Spruch schon kennt, und andernfalls der
-* Spruch zu seiner List-of-known-spells hinzugefügt werden.
-*/
-
-static int read_seenspell(attrib * a, void *owner, struct gamedata *data)
-{
-    storage *store = data->store;
-    spell *sp = 0;
-    char token[32];
-
-    UNUSED_ARG(owner);
-    READ_TOK(store, token, sizeof(token));
-    if (data->version < UNIQUE_SPELLS_VERSION) {
-        READ_INT(store, 0); /* ignore mtype */
-    }
-    sp = find_spell(token);
-    if (!sp) {
-        log_info("read_seenspell: could not find spell '%s'\n", token);
-        return AT_READ_FAIL;
-    }
-    a->data.v = sp;
-    return AT_READ_OK;
-}
-
-static void
-write_seenspell(const attrib * a, const void *owner, struct storage *store)
-{
-    const spell *sp = (const spell *)a->data.v;
-    UNUSED_ARG(owner);
-    WRITE_TOK(store, sp->sname);
-}
-
-attrib_type at_seenspell = {
-    "seenspell", NULL, NULL, NULL, write_seenspell, read_seenspell
-};
-
 #define MAXSPELLS 256
 
-static bool already_seen(const faction * f, const spell * sp)
-{
-    attrib *a;
-
-    for (a = a_find(f->attribs, &at_seenspell); a && a->type == &at_seenspell;
-        a = a->next) {
-        if (a->data.v == sp)
-            return true;
-    }
-    return false;
-}
-
-void show_new_spells(faction * f, int level, const spellbook *book)
-{
-    if (book) {
-        selist *ql = book->spells;
-        int qi;
-
-        for (qi = 0; ql; selist_advance(&ql, &qi, 1)) {
-            spellbook_entry *sbe = (spellbook_entry *)selist_get(ql, qi);
-            if (sbe->level <= level) {
-                if (!already_seen(f, sbe->sp)) {
-                    attrib * a = a_new(&at_reportspell);
-                    spellbook_entry * entry = (spellbook_entry *)a->data.v;
-                    entry->level = sbe->level;
-                    entry->sp = sbe->sp;
-                    a_add(&f->attribs, a);
-                    a_add(&f->attribs, a_new(&at_seenspell))->data.v = sbe->sp;
-                }
-            }
-        }
-    }
-}
-
 /** update the spellbook with a new level
 * Written for E3
 */
diff --git a/src/magic.h b/src/magic.h
index 403dafdc6..101ef5d38 100644
--- a/src/magic.h
+++ b/src/magic.h
@@ -191,13 +191,11 @@ extern "C" {
 
     void regenerate_aura(void);
 
-    extern struct attrib_type at_seenspell;
     extern struct attrib_type at_mage;
     extern struct attrib_type at_familiarmage;
     extern struct attrib_type at_familiar;
     extern struct attrib_type at_clonemage;
     extern struct attrib_type at_clone;
-    extern struct attrib_type at_reportspell;
     extern struct attrib_type at_icastle;
 
     void make_icastle(struct building *b, const struct building_type *btype, int timeout);
diff --git a/src/report.c b/src/report.c
index 6543b153b..30ea34fdf 100644
--- a/src/report.c
+++ b/src/report.c
@@ -39,6 +39,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include <attributes/overrideroads.h>
 #include <attributes/otherfaction.h>
 #include <attributes/reduceproduction.h>
+#include <attributes/seenspell.h>
 
 /* gamecode includes */
 #include "alchemy.h"

From cfc3171021a6317109fc375d9af5d4f2241b5e8b Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Thu, 8 Feb 2018 19:37:47 +0100
Subject: [PATCH 07/40] constrict the seenspell module a bit more, move
 functionality inside.

---
 src/attributes/seenspell.c | 64 +++++++++++++++++---------------------
 src/attributes/seenspell.h |  6 ++--
 src/laws.c                 | 25 ++++++++++-----
 src/magic.h                |  1 -
 4 files changed, 49 insertions(+), 47 deletions(-)

diff --git a/src/attributes/seenspell.c b/src/attributes/seenspell.c
index 99ac84873..555d7925c 100644
--- a/src/attributes/seenspell.c
+++ b/src/attributes/seenspell.c
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 1998-2014,
+Copyright (c) 1998-2018,
 Enno Rehling <enno@eressea.de>
 Katja Zedel <katze@felidae.kn-bremen.de
 Christian Schlittchen <corwin@amber.kn-bremen.de>
@@ -37,11 +37,11 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 /* ------------------------------------------------------------- */
 /* Ausgabe der Spruchbeschreibungen
-* Anzeige des Spruchs nur, wenn die Stufe des besten Magiers vorher
-* kleiner war (u->faction->seenspells). Ansonsten muss nur geprüft
-* werden, ob dieser Magier den Spruch schon kennt, und andernfalls der
-* Spruch zu seiner List-of-known-spells hinzugefügt werden.
-*/
+ * Anzeige des Spruchs nur, wenn die Stufe des besten Magiers vorher
+ * kleiner war (u->faction->seenspells). Ansonsten muss nur geprüft
+ * werden, ob dieser Magier den Spruch schon kennt, und andernfalls der
+ * Spruch zu seiner List-of-known-spells hinzugefügt werden.
+ */
 
 static int read_seenspell(attrib * a, void *owner, struct gamedata *data)
 {
@@ -87,40 +87,32 @@ static bool already_seen(const faction * f, const spell * sp)
     return false;
 }
 
-static void a_init_reportspell(struct attrib *a) {
-    a->data.v = calloc(1, sizeof(spellbook_entry));
-}
-
-static void a_finalize_reportspell(struct attrib *a) {
-    free(a->data.v);
-}
-
 attrib_type at_reportspell = {
-    "reportspell",
-    a_init_reportspell,
-    a_finalize_reportspell,
-    0, NO_WRITE, NO_READ
+    "reportspell", NULL
 };
 
-void show_new_spells(faction * f, int level, const spellbook *book)
+void show_spell(faction *f, const spellbook_entry *sbe)
 {
-    if (book) {
-        selist *ql = book->spells;
-        int qi;
-
-        for (qi = 0; ql; selist_advance(&ql, &qi, 1)) {
-            spellbook_entry *sbe = (spellbook_entry *)selist_get(ql, qi);
-            if (sbe->level <= level) {
-                if (!already_seen(f, sbe->sp)) {
-                    attrib * a = a_new(&at_reportspell);
-                    spellbook_entry * entry = (spellbook_entry *)a->data.v;
-                    entry->level = sbe->level;
-                    entry->sp = sbe->sp;
-                    a_add(&f->attribs, a);
-                    a_add(&f->attribs, a_new(&at_seenspell))->data.v = sbe->sp;
-                }
-            }
-        }
+    if (!already_seen(f, sbe->sp)) {
+        attrib * a = a_new(&at_reportspell);
+        a->data.v = (void *)sbe;
+        a_add(&f->attribs, a);
+        a_add(&f->attribs, a_new(&at_seenspell))->data.v = sbe->sp;
     }
 }
 
+void reset_seen_spells(faction *f, const struct spell *sp)
+{
+    if (sp) {
+        attrib *a = a_find(f->attribs, &at_seenspell);
+        while (a && a->type == &at_seenspell && a->data.v != sp) {
+            a = a->next;
+        }
+        if (a) {
+            a_remove(&f->attribs, a);
+        }
+    }
+    else {
+        a_removeall(&f->attribs, &at_seenspell);
+    }
+}
diff --git a/src/attributes/seenspell.h b/src/attributes/seenspell.h
index 488430575..aada5ad0a 100644
--- a/src/attributes/seenspell.h
+++ b/src/attributes/seenspell.h
@@ -2,10 +2,12 @@
 #define H_SEENSPELL
 
 struct attrib_type;
-struct spellbook;
+struct spellbook_entry;
 struct faction;
+struct spell;
 
-void show_new_spells(struct faction * f, int level, const struct spellbook *book);
+void show_spell(struct faction * f, const struct spellbook_entry *sbe);
+void reset_seen_spells(struct faction * f, const struct spell *sp);
 
 extern struct attrib_type at_reportspell;
 extern struct attrib_type at_seenspell;
diff --git a/src/laws.c b/src/laws.c
index 9e2eb9471..ac8b3d612 100644
--- a/src/laws.c
+++ b/src/laws.c
@@ -2307,13 +2307,7 @@ static void reshow_other(unit * u, struct order *ord, const char *s) {
         }
 
         if (sp) {
-            attrib *a = a_find(u->faction->attribs, &at_seenspell);
-            while (a != NULL && a->type == &at_seenspell && a->data.v != sp) {
-                a = a->next;
-            }
-            if (a != NULL) {
-                a_remove(&u->faction->attribs, a);
-            }
+            reset_seen_spells(u->faction, sp);
             found = true;
         }
 
@@ -2331,7 +2325,7 @@ static void reshow(unit * u, struct order *ord, const char *s, param_t p)
 {
     switch (p) {
     case P_ZAUBER:
-        a_removeall(&u->faction->attribs, &at_seenspell);
+        reset_seen_spells(u->faction, NULL);
         break;
     case P_POTIONS:
         if (!display_potions(u)) {
@@ -3304,6 +3298,21 @@ static void copy_spells(const spellbook * src, spellbook * dst, int maxlevel)
     }
 }
 
+static void show_new_spells(faction * f, int level, const spellbook *book)
+{
+    if (book) {
+        selist *ql = book->spells;
+        int qi;
+
+        for (qi = 0; ql; selist_advance(&ql, &qi, 1)) {
+            spellbook_entry *sbe = (spellbook_entry *)selist_get(ql, qi);
+            if (sbe->level <= level) {
+                show_spell(f, sbe);
+            }
+        }
+    }
+}
+
 static void update_spells(void)
 {
     faction *f;
diff --git a/src/magic.h b/src/magic.h
index 101ef5d38..cbd508163 100644
--- a/src/magic.h
+++ b/src/magic.h
@@ -241,7 +241,6 @@ extern "C" {
     int u_hasspell(const struct unit *u, const struct spell *sp);
     /* pr�ft, ob der Spruch in der Spruchliste der Einheit steht. */
     void pick_random_spells(struct faction *f, int level, struct spellbook * book, int num_spells);
-    void show_new_spells(struct faction * f, int level, const struct spellbook *book);
     bool knowsspell(const struct region *r, const struct unit *u,
         const struct spell * sp);
     /* pr�ft, ob die Einheit diesen Spruch gerade beherrscht, dh

From 1035a98fd3049e7f7a71c3cbb3de2186a37095fe Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Fri, 9 Feb 2018 21:20:43 +0100
Subject: [PATCH 08/40] make attrib use a variant, not a custom union. change
 attrib_type methods to take a variant, not the entire attrib.

---
 src/alchemy.c                 | 29 ++++++----------
 src/attributes/attributes.c   | 30 +++++++++-------
 src/attributes/dict.c         | 16 ++++-----
 src/attributes/follow.c       |  2 +-
 src/attributes/hate.c         |  8 ++---
 src/attributes/key.c          | 18 ++++------
 src/attributes/moved.c        | 10 +++---
 src/attributes/movement.c     | 14 +++-----
 src/attributes/otherfaction.c | 10 +++---
 src/attributes/seenspell.c    |  8 ++---
 src/attributes/targetregion.c |  8 ++---
 src/economy.c                 |  6 ++--
 src/helpers.c                 |  4 +--
 src/kernel/ally.c             |  4 +--
 src/kernel/curse.c            | 16 ++++-----
 src/kernel/curse.h            |  8 ++---
 src/kernel/group.c            |  8 ++---
 src/kernel/region.c           | 12 +++----
 src/kernel/skills.c           | 11 ++----
 src/kernel/unit.c             | 14 ++++----
 src/magic.c                   | 51 +++++++++++++---------------
 src/market.c                  |  6 ++--
 src/modules/gmcmd.c           |  9 +++--
 src/modules/museum.c          | 41 ++++++++++------------
 src/move.c                    | 19 ++++-------
 src/piracy.c                  | 11 ++----
 src/spells.c                  |  4 +--
 src/spells/borders.c          |  8 ++---
 src/study.c                   | 10 +++---
 src/travelthru.c              |  5 ++-
 src/util/attrib.c             | 64 +++++++++++++++++++----------------
 src/util/attrib.h             | 43 ++++++++++-------------
 src/util/attrib.test.c        | 58 +++++++++++++++----------------
 src/util/event.c              | 16 ++++-----
 src/vortex.c                  | 16 ++++-----
 src/wormhole.c                |  8 ++---
 36 files changed, 282 insertions(+), 323 deletions(-)

diff --git a/src/alchemy.c b/src/alchemy.c
index 75212e34a..3ec21159d 100644
--- a/src/alchemy.c
+++ b/src/alchemy.c
@@ -284,13 +284,9 @@ typedef struct potiondelay {
     int amount;
 } potiondelay;
 
-static void init_potiondelay(attrib * a)
+static void init_potiondelay(variant *var)
 {
-    a->data.v = malloc(sizeof(potiondelay));
-}
-
-static void free_potiondelay(attrib * a) {
-    free(a->data.v);
+    var->v = malloc(sizeof(potiondelay));
 }
 
 static int age_potiondelay(attrib * a, void *owner)
@@ -304,7 +300,7 @@ static int age_potiondelay(attrib * a, void *owner)
 attrib_type at_potiondelay = {
     "potiondelay",
     init_potiondelay,
-    free_potiondelay,
+    a_free_voidptr,
     age_potiondelay, 0, 0
 };
 
@@ -337,31 +333,26 @@ struct order *ord)
 /*   at_effect   */
 /*****************/
 
-static void a_initeffect(attrib * a)
+static void a_initeffect(variant *var)
 {
-    a->data.v = calloc(sizeof(effect_data), 1);
-}
-
-static void a_finalizeeffect(attrib * a) /*-V524 */
-{
-    free(a->data.v);
+    var->v = calloc(sizeof(effect_data), 1);
 }
 
 static void
-a_writeeffect(const attrib * a, const void *owner, struct storage *store)
+a_writeeffect(const variant *var, const void *owner, struct storage *store)
 {
-    effect_data *edata = (effect_data *)a->data.v;
+    effect_data *edata = (effect_data *)var->v;
     UNUSED_ARG(owner);
     WRITE_TOK(store, resourcename(edata->type->rtype, 0));
     WRITE_INT(store, edata->value);
 }
 
-static int a_readeffect(attrib * a, void *owner, struct gamedata *data)
+static int a_readeffect(variant *var, void *owner, struct gamedata *data)
 {
     struct storage *store = data->store;
     int power;
     const resource_type *rtype;
-    effect_data *edata = (effect_data *)a->data.v;
+    effect_data *edata = (effect_data *)var->v;
     char zText[32];
 
     UNUSED_ARG(owner);
@@ -386,7 +377,7 @@ static int a_readeffect(attrib * a, void *owner, struct gamedata *data)
 attrib_type at_effect = {
     "effect",
     a_initeffect,
-    a_finalizeeffect,
+    a_free_voidptr,
     DEFAULT_AGE,
     a_writeeffect,
     a_readeffect,
diff --git a/src/attributes/attributes.c b/src/attributes/attributes.c
index cb6e335b5..e6887a777 100644
--- a/src/attributes/attributes.c
+++ b/src/attributes/attributes.c
@@ -68,42 +68,45 @@ typedef struct obs_data {
     int timer;
 } obs_data;
 
-static void obs_init(struct attrib *a)
+static void obs_init(variant *var)
 {
-    a->data.v = malloc(sizeof(obs_data));
-}
-
-static void obs_done(struct attrib *a)
-{
-    free(a->data.v);
+    var->v = malloc(sizeof(obs_data));
 }
 
 static int obs_age(struct attrib *a, void *owner)
 {
     obs_data *od = (obs_data *)a->data.v;
+
+    UNUSED_ARG(owner);
     update_interval(od->f, (region *)owner);
     return --od->timer;
 }
 
-static void obs_write(const struct attrib *a, const void *owner, struct storage *store)
+static void obs_write(const variant *var, const void *owner,
+    struct storage *store)
 {
-    obs_data *od = (obs_data *)a->data.v;
+    obs_data *od = (obs_data *)var->v;
+
+    UNUSED_ARG(owner);
     write_faction_reference(od->f, store);
     WRITE_INT(store, od->skill);
     WRITE_INT(store, od->timer);
 }
 
-static int obs_read(struct attrib *a, void *owner, struct gamedata *data)
+static int obs_read(variant *var, void *owner, struct gamedata *data)
 {
-    obs_data *od = (obs_data *)a->data.v;
+    obs_data *od = (obs_data *)var->v;
 
+    UNUSED_ARG(owner);
     read_faction_reference(data, &od->f, NULL);
     READ_INT(data->store, &od->skill);
     READ_INT(data->store, &od->timer);
     return AT_READ_OK;
 }
 
-attrib_type at_observer = { "observer", obs_init, obs_done, obs_age, obs_write, obs_read };
+attrib_type at_observer = {
+    "observer", obs_init, a_free_voidptr, obs_age, obs_write, obs_read
+};
 
 static attrib *make_observer(faction *f, int perception)
 {
@@ -154,10 +157,11 @@ attrib_type at_unitdissolve = {
     "unitdissolve", NULL, NULL, NULL, a_writechars, a_readchars
 };
 
-static int read_ext(attrib * a, void *owner, gamedata *data)
+static int read_ext(variant *var, void *owner, gamedata *data)
 {
     int len;
 
+    UNUSED_ARG(var);
     READ_INT(data->store, &len);
     data->store->api->r_bin(data->store->handle, NULL, (size_t)len);
     return AT_READ_OK;
diff --git a/src/attributes/dict.c b/src/attributes/dict.c
index 83a53dbeb..d3c5ac329 100644
--- a/src/attributes/dict.c
+++ b/src/attributes/dict.c
@@ -62,11 +62,11 @@ typedef struct dict_data {
     } data;
 } dict_data;
 
-static int dict_read(attrib * a, void *owner, gamedata *data)
+static int dict_read(variant * var, void *owner, gamedata *data)
 {
     storage *store = data->store;
     char name[NAMESIZE];
-    dict_data *dd = (dict_data *)a->data.v;
+    dict_data *dd = (dict_data *)var->v;
     int n;
 
     READ_STR(store, name, sizeof(name));
@@ -88,19 +88,19 @@ static int dict_read(attrib * a, void *owner, gamedata *data)
     return AT_READ_DEPR;
 }
 
-static void dict_init(attrib * a)
+static void dict_init(variant *var)
 {
     dict_data *dd;
-    a->data.v = malloc(sizeof(dict_data));
-    dd = (dict_data *)a->data.v;
+    var->v = malloc(sizeof(dict_data));
+    dd = (dict_data *)var->v;
     dd->type = TNONE;
 }
 
-static void dict_done(attrib * a)
+static void dict_done(variant *var)
 {
-    dict_data *dd = (dict_data *)a->data.v;
+    dict_data *dd = (dict_data *)var->v;
     free(dd->name);
-    free(a->data.v);
+    free(var->v);
 }
 
 static void upgrade_keyval(const dict_data *dd, int keyval[], int v) {
diff --git a/src/attributes/follow.c b/src/attributes/follow.c
index e0741646b..8c5e14526 100644
--- a/src/attributes/follow.c
+++ b/src/attributes/follow.c
@@ -28,7 +28,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 #include <storage.h>
 
-static int read_follow(attrib * a, void *owner, gamedata *data)
+static int read_follow(variant * var, void *owner, gamedata *data)
 {
     READ_INT(data->store, NULL);   /* skip it */
     return AT_READ_FAIL;
diff --git a/src/attributes/hate.c b/src/attributes/hate.c
index e6751bdfb..c260de335 100644
--- a/src/attributes/hate.c
+++ b/src/attributes/hate.c
@@ -39,14 +39,14 @@ static int verify_hate(attrib * a, void *owner)
 }
 
 static void
-write_hate(const attrib * a, const void *owner, struct storage *store)
+write_hate(const variant *var, const void *owner, struct storage *store)
 {
-    write_unit_reference((unit *)a->data.v, store);
+    write_unit_reference((unit *)var->v, store);
 }
 
-static int read_hate(attrib * a, void *owner, gamedata *data)
+static int read_hate(variant *var, void *owner, gamedata *data)
 {
-    if (read_unit_reference(data, (unit **)&a->data.v, NULL) <= 0) {
+    if (read_unit_reference(data, (unit **)&var->v, NULL) <= 0) {
         return AT_READ_FAIL;
     }
     return AT_READ_OK;
diff --git a/src/attributes/key.c b/src/attributes/key.c
index 61bbb4599..75958a6fb 100644
--- a/src/attributes/key.c
+++ b/src/attributes/key.c
@@ -29,8 +29,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include <string.h>
 #include <assert.h>
 
-static void a_writekeys(const attrib *a, const void *o, storage *store) {
-    int i, *keys = (int *)a->data.v;
+static void a_writekeys(const variant *var, const void *o, storage *store) {
+    int i, *keys = (int *)var->v;
     int n = 0;
     if (keys) {
         assert(keys[0] < 4096 && keys[0]>0);
@@ -76,7 +76,7 @@ static int keys_size(int n) {
     return 4096;
 }
 
-static int a_readkeys(attrib * a, void *owner, gamedata *data) {
+static int a_readkeys(variant *var, void *owner, gamedata *data) {
     int i, n, *keys;
 
     READ_INT(data->store, &n);
@@ -135,26 +135,22 @@ static int a_readkeys(attrib * a, void *owner, gamedata *data) {
             }
         }
     }
-    a->data.v = keys;
+    var->v = keys;
     return AT_READ_OK;
 }
 
-static int a_readkey(attrib *a, void *owner, struct gamedata *data) {
-    int res = a_readint(a, owner, data);
+static int a_readkey(variant *var, void *owner, struct gamedata *data) {
+    int res = a_readint(var, owner, data);
     if (data->version >= KEYVAL_VERSION) {
         return AT_READ_FAIL;
     }
     return (res != AT_READ_FAIL) ? AT_READ_DEPR : res;
 }
 
-static void a_freekeys(attrib *a) {
-    free(a->data.v);
-}
-
 attrib_type at_keys = {
     "keys",
     NULL,
-    a_freekeys,
+    a_free_voidptr,
     NULL,
     a_writekeys,
     a_readkeys,
diff --git a/src/attributes/moved.c b/src/attributes/moved.c
index e34b9c5e8..abff4ac73 100644
--- a/src/attributes/moved.c
+++ b/src/attributes/moved.c
@@ -34,15 +34,15 @@ static int age_moved(attrib * a, void *owner)
 }
 
 static void
-write_moved(const attrib * a, const void *owner, struct storage *store)
+write_moved(const variant *var, const void *owner, struct storage *store)
 {
-    WRITE_INT(store, a->data.i);
+    WRITE_INT(store, var->i);
 }
 
-static int read_moved(attrib * a, void *owner, gamedata *data)
+static int read_moved(variant *var, void *owner, gamedata *data)
 {
-    READ_INT(data->store, &a->data.i);
-    if (a->data.i != 0)
+    READ_INT(data->store, &var->i);
+    if (var->i != 0)
         return AT_READ_OK;
     else
         return AT_READ_FAIL;
diff --git a/src/attributes/movement.c b/src/attributes/movement.c
index 1b5ba3f94..7b49291c2 100644
--- a/src/attributes/movement.c
+++ b/src/attributes/movement.c
@@ -29,23 +29,17 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include <limits.h>
 #include <assert.h>
 
-static void
-write_movement(const attrib * a, const void *owner, struct storage *store)
+static int read_movement(variant *var, void *owner, gamedata *data)
 {
-    WRITE_INT(store, a->data.i);
-}
-
-static int read_movement(attrib * a, void *owner, gamedata *data)
-{
-    READ_INT(data->store, &a->data.i);
-    if (a->data.i != 0)
+    READ_INT(data->store, &var->i);
+    if (var->i != 0)
         return AT_READ_OK;
     else
         return AT_READ_FAIL;
 }
 
 attrib_type at_movement = {
-    "movement", NULL, NULL, NULL, write_movement, read_movement
+    "movement", NULL, NULL, NULL, a_writeint, read_movement
 };
 
 bool get_movement(attrib * const *alist, int type)
diff --git a/src/attributes/otherfaction.c b/src/attributes/otherfaction.c
index c9e56fe27..343460057 100644
--- a/src/attributes/otherfaction.c
+++ b/src/attributes/otherfaction.c
@@ -33,20 +33,20 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  * simple attributes that do not yet have their own file
  */
 
-void write_of(const struct attrib *a, const void *owner, struct storage *store)
+void write_of(const variant *var, const void *owner, struct storage *store)
 {
-    const faction *f = (faction *)a->data.v;
+    const faction *f = (faction *)var->v;
     WRITE_INT(store, f->no);
 }
 
-int read_of(struct attrib *a, void *owner, gamedata *data)
+int read_of(variant *var, void *owner, gamedata *data)
 {                               /* return 1 on success, 0 if attrib needs removal */
     int of;
 
     READ_INT(data->store, &of);
     if (rule_stealth_other()) {
-        a->data.v = findfaction(of);
-        if (a->data.v) {
+        var->v = findfaction(of);
+        if (var->v) {
             return AT_READ_OK;
         }
     }
diff --git a/src/attributes/seenspell.c b/src/attributes/seenspell.c
index 555d7925c..e7fd2e38a 100644
--- a/src/attributes/seenspell.c
+++ b/src/attributes/seenspell.c
@@ -43,7 +43,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  * Spruch zu seiner List-of-known-spells hinzugefügt werden.
  */
 
-static int read_seenspell(attrib * a, void *owner, struct gamedata *data)
+static int read_seenspell(variant *var, void *owner, struct gamedata *data)
 {
     storage *store = data->store;
     spell *sp = 0;
@@ -59,14 +59,14 @@ static int read_seenspell(attrib * a, void *owner, struct gamedata *data)
         log_info("read_seenspell: could not find spell '%s'\n", token);
         return AT_READ_FAIL;
     }
-    a->data.v = sp;
+    var->v = sp;
     return AT_READ_OK;
 }
 
 static void
-write_seenspell(const attrib * a, const void *owner, struct storage *store)
+write_seenspell(const variant *var, const void *owner, struct storage *store)
 {
-    const spell *sp = (const spell *)a->data.v;
+    const spell *sp = (const spell *)var->v;
     UNUSED_ARG(owner);
     WRITE_TOK(store, sp->sname);
 }
diff --git a/src/attributes/targetregion.c b/src/attributes/targetregion.c
index 29e76f557..7ef1fe84a 100644
--- a/src/attributes/targetregion.c
+++ b/src/attributes/targetregion.c
@@ -29,14 +29,14 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include <storage.h>
 
 static void
-write_targetregion(const attrib * a, const void *owner, struct storage *store)
+write_targetregion(const variant *var, const void *owner, struct storage *store)
 {
-    write_region_reference((region *)a->data.v, store);
+    write_region_reference((region *)var->v, store);
 }
 
-static int read_targetregion(attrib * a, void *owner, gamedata *data)
+static int read_targetregion(variant *var, void *owner, gamedata *data)
 {
-    if (read_region_reference(data, (region **)&a->data.v, NULL) <= 0) {
+    if (read_region_reference(data, (region **)&var->v, NULL) <= 0) {
         return AT_READ_FAIL;
     }
     return AT_READ_OK;
diff --git a/src/economy.c b/src/economy.c
index 82890888e..a992729f4 100644
--- a/src/economy.c
+++ b/src/economy.c
@@ -1432,10 +1432,10 @@ int make_cmd(unit * u, struct order *ord)
 
 /* ------------------------------------------------------------- */
 
-static void free_luxuries(struct attrib *a)
+static void free_luxuries(variant *var)
 {
-    item *itm = (item *)a->data.v;
-    a->data.v = NULL;
+    item *itm = (item *)var->v;
+    var->v = NULL;
     i_freeall(&itm);
 }
 
diff --git a/src/helpers.c b/src/helpers.c
index b5c4c05ba..e6015488a 100644
--- a/src/helpers.c
+++ b/src/helpers.c
@@ -306,12 +306,12 @@ struct trigger_type tt_caldera = {
 };
 
 
-static int building_action_read(struct attrib *a, void *owner, gamedata *data)
+static int building_action_read(variant *var, void *owner, gamedata *data)
 {
     struct storage *store = data->store;
 
     UNUSED_ARG(owner);
-    UNUSED_ARG(a);
+    UNUSED_ARG(var);
 
     if (data->version < ATTRIBOWNER_VERSION) {
         READ_INT(data->store, NULL);
diff --git a/src/kernel/ally.c b/src/kernel/ally.c
index dbb6aff3a..8f99f1f07 100644
--- a/src/kernel/ally.c
+++ b/src/kernel/ally.c
@@ -136,9 +136,9 @@ static int ally_mode(const ally * sf, int mode)
     return sf->status & mode;
 }
 
-static void init_npcfaction(struct attrib *a)
+static void init_npcfaction(variant *var)
 {
-    a->data.i = 1;
+    var->i = 1;
 }
 
 attrib_type at_npcfaction = {
diff --git a/src/kernel/curse.c b/src/kernel/curse.c
index 0f215dbee..d03ddb067 100644
--- a/src/kernel/curse.c
+++ b/src/kernel/curse.c
@@ -102,9 +102,9 @@ static void cunhash(curse * c)
 
 /* ------------------------------------------------------------- */
 /* at_curse */
-void curse_init(attrib * a)
+void curse_init(variant *var)
 {
-    a->data.v = calloc(1, sizeof(curse));
+    var->v = calloc(1, sizeof(curse));
 }
 
 int curse_age(attrib * a, void *owner)
@@ -133,9 +133,9 @@ void destroy_curse(curse * c)
     free(c);
 }
 
-void curse_done(attrib * a)
+void curse_done(variant * var)
 {
-    destroy_curse((curse *)a->data.v);
+    destroy_curse((curse *)var->v);
 }
 
 /** reads curses that have been removed from the code */
@@ -177,10 +177,10 @@ static int read_ccompat(const char *cursename, struct storage *store)
     return -1;
 }
 
-int curse_read(attrib * a, void *owner, gamedata *data)
+int curse_read(variant *var, void *owner, gamedata *data)
 {
     storage *store = data->store;
-    curse *c = (curse *)a->data.v;
+    curse *c = (curse *)var->v;
     int ur;
     char cursename[64];
     int n;
@@ -238,9 +238,9 @@ int curse_read(attrib * a, void *owner, gamedata *data)
     return AT_READ_OK;
 }
 
-void curse_write(const attrib * a, const void *owner, struct storage *store)
+void curse_write(const variant * var, const void *owner, struct storage *store)
 {
-    curse *c = (curse *)a->data.v;
+    curse *c = (curse *)var->v;
     const curse_type *ct = c->type;
     unit *mage = (c->magician && c->magician->number) ? c->magician : NULL;
 
diff --git a/src/kernel/curse.h b/src/kernel/curse.h
index 054387d59..36ace2895 100644
--- a/src/kernel/curse.h
+++ b/src/kernel/curse.h
@@ -221,9 +221,9 @@ extern "C" {
 
     void curses_done(void); /* de-register all curse-types */
 
-    void curse_write(const struct attrib *a, const void *owner,
+    void curse_write(const union variant *v, const void *owner,
         struct storage *store);
-    int curse_read(struct attrib *a, void *owner, struct gamedata *store);
+    int curse_read(union variant *v, void *owner, struct gamedata *store);
 
     /* ------------------------------------------------------------- */
     /* Kommentare:
@@ -291,8 +291,8 @@ extern "C" {
 
     curse *findcurse(int curseid);
 
-    void curse_init(struct attrib *a);
-    void curse_done(struct attrib *a);
+    void curse_init(union variant *a);
+    void curse_done(union variant *a);
     int curse_age(struct attrib *a, void *owner);
 
     double destr_curse(struct curse *c, int cast_level, double force);
diff --git a/src/kernel/group.c b/src/kernel/group.c
index 54cd965da..48434ff5a 100755
--- a/src/kernel/group.c
+++ b/src/kernel/group.c
@@ -96,14 +96,14 @@ static group *find_group(int gid)
     return g;
 }
 
-static int read_group(attrib * a, void *owner, gamedata *data)
+static int read_group(variant *var, void *owner, gamedata *data)
 {
     struct storage *store = data->store;
     group *g;
     int gid;
 
     READ_INT(store, &gid);
-    a->data.v = g = find_group(gid);
+    var->v = g = find_group(gid);
     if (g != 0) {
         g->members++;
         return AT_READ_OK;
@@ -112,9 +112,9 @@ static int read_group(attrib * a, void *owner, gamedata *data)
 }
 
 static void
-write_group(const attrib * a, const void *owner, struct storage *store)
+write_group(const variant *var, const void *owner, struct storage *store)
 {
-    group *g = (group *)a->data.v;
+    group *g = (group *)var->v;
     WRITE_INT(store, g->gid);
 }
 
diff --git a/src/kernel/region.c b/src/kernel/region.c
index 3f25f156b..28d4e8bee 100644
--- a/src/kernel/region.c
+++ b/src/kernel/region.c
@@ -181,14 +181,14 @@ void deathcounts(region * r, int fallen)
 /********************/
 /*   at_moveblock   */
 /********************/
-void a_initmoveblock(attrib * a)
+void a_initmoveblock(variant *var)
 {
-    a->data.v = calloc(1, sizeof(moveblock));
+    var->v = calloc(1, sizeof(moveblock));
 }
 
-int a_readmoveblock(attrib * a, void *owner, gamedata *data)
+int a_readmoveblock(variant *var, void *owner, gamedata *data)
 {
-    moveblock *m = (moveblock *)(a->data.v);
+    moveblock *m = (moveblock *)var->v;
     int i;
 
     READ_INT(data->store, &i);
@@ -197,9 +197,9 @@ int a_readmoveblock(attrib * a, void *owner, gamedata *data)
 }
 
 void
-a_writemoveblock(const attrib * a, const void *owner, struct storage *store)
+a_writemoveblock(const variant *var, const void *owner, struct storage *store)
 {
-    moveblock *m = (moveblock *)(a->data.v);
+    moveblock *m = (moveblock *)var->v;
     WRITE_INT(store, (int)m->dir);
 }
 
diff --git a/src/kernel/skills.c b/src/kernel/skills.c
index 8c4b4d2a6..24981728f 100644
--- a/src/kernel/skills.c
+++ b/src/kernel/skills.c
@@ -40,21 +40,16 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include <string.h>
 
 /** skillmod attribut **/
-static void init_skillmod(attrib * a)
+static void init_skillmod(variant *var)
 {
-    a->data.v = calloc(sizeof(skillmod_data), 1);
-}
-
-static void finalize_skillmod(attrib * a)
-{
-    free(a->data.v);
+    var->v = calloc(sizeof(skillmod_data), 1);
 }
 
 /** temporary skill modification (NOT SAVED!). */
 attrib_type at_skillmod = {
     "skillmod",
     init_skillmod,
-    finalize_skillmod,
+    a_free_voidptr,
     NULL,
     NULL,                         /* can't write function pointers */
     NULL,                         /* can't read function pointers */
diff --git a/src/kernel/unit.c b/src/kernel/unit.c
index 367ea4d97..acc28fff3 100644
--- a/src/kernel/unit.c
+++ b/src/kernel/unit.c
@@ -506,13 +506,13 @@ int ualias(const unit * u)
     return a->data.i;
 }
 
-int a_readprivate(attrib * a, void *owner, gamedata *data)
+int a_readprivate(variant *var, void *owner, gamedata *data)
 {
     struct storage *store = data->store;
     char lbuf[DISPLAYSIZE];
     READ_STR(store, lbuf, sizeof(lbuf));
-    a->data.v = str_strdup(lbuf);
-    return (a->data.v) ? AT_READ_OK : AT_READ_FAIL;
+    var->v = str_strdup(lbuf);
+    return (var->v) ? AT_READ_OK : AT_READ_FAIL;
 }
 
 /*********************/
@@ -644,15 +644,15 @@ void usettarget(unit * u, const unit * t)
 /*   at_siege   */
 /*********************/
 
-void a_writesiege(const attrib * a, const void *owner, struct storage *store)
+void a_writesiege(const variant *var, const void *owner, struct storage *store)
 {
-    struct building *b = (struct building *)a->data.v;
+    struct building *b = (struct building *)var->v;
     write_building_reference(b, store);
 }
 
-int a_readsiege(attrib * a, void *owner, gamedata *data)
+int a_readsiege(variant *var, void *owner, gamedata *data)
 {
-    if (read_building_reference(data, (building **)&a->data.v, NULL) <= 0) {
+    if (read_building_reference(data, (building **)&var->v, NULL) <= 0) {
         return AT_READ_FAIL;
     }
     return AT_READ_OK;
diff --git a/src/magic.c b/src/magic.c
index 4e9c87b4e..5cfd4d071 100644
--- a/src/magic.c
+++ b/src/magic.c
@@ -126,10 +126,10 @@ typedef struct icastle_data {
     int time;
 } icastle_data;
 
-static int a_readicastle(attrib * a, void *owner, struct gamedata *data)
+static int a_readicastle(variant *var, void *owner, struct gamedata *data)
 {
     storage *store = data->store;
-    icastle_data *idata = (icastle_data *)a->data.v;
+    icastle_data *idata = (icastle_data *)var->v;
     char token[32];
 
     UNUSED_ARG(owner);
@@ -143,9 +143,9 @@ static int a_readicastle(attrib * a, void *owner, struct gamedata *data)
 }
 
 static void
-a_writeicastle(const attrib * a, const void *owner, struct storage *store)
+a_writeicastle(const variant *var, const void *owner, struct storage *store)
 {
-    icastle_data *data = (icastle_data *)a->data.v;
+    icastle_data *data = (icastle_data *)var->v;
     UNUSED_ARG(owner);
     WRITE_TOK(store, data->type->_name);
     WRITE_INT(store, data->time);
@@ -168,20 +168,15 @@ static int a_ageicastle(struct attrib *a, void *owner)
     return AT_AGE_KEEP;
 }
 
-static void a_initicastle(struct attrib *a)
+static void a_initicastle(variant *var)
 {
-    a->data.v = calloc(sizeof(icastle_data), 1);
-}
-
-static void a_finalizeicastle(struct attrib *a) /*-V524 */
-{
-    free(a->data.v);
+    var->v = calloc(sizeof(icastle_data), 1);
 }
 
 attrib_type at_icastle = {
     "zauber_icastle",
     a_initicastle,
-    a_finalizeicastle,
+    a_free_voidptr,
     a_ageicastle,
     a_writeicastle,
     a_readicastle
@@ -207,14 +202,14 @@ extern int dice(int count, int value);
  * Umwandlung von alt nach neu gebraucht werden */
  /* ------------------------------------------------------------- */
 
-static void init_mage(attrib * a)
+static void init_mage(variant *var)
 {
-    a->data.v = calloc(sizeof(sc_mage), 1);
+    var->v = calloc(sizeof(sc_mage), 1);
 }
 
-static void free_mage(attrib * a)
+static void free_mage(variant *var)
 {
-    sc_mage *mage = (sc_mage *)a->data.v;
+    sc_mage *mage = (sc_mage *)var->v;
     if (mage->spellbook) {
         spellbook_clear(mage->spellbook);
         free(mage->spellbook);
@@ -239,11 +234,11 @@ int get_spell_level_mage(const spell * sp, void * cbdata)
     return sbe ? sbe->level : 0;
 }
 
-static int read_mage(attrib * a, void *owner, struct gamedata *data)
+static int read_mage(variant *var, void *owner, struct gamedata *data)
 {
     storage *store = data->store;
     int i, mtype;
-    sc_mage *mage = (sc_mage *)a->data.v;
+    sc_mage *mage = (sc_mage *)var->v;
     char spname[64];
 
     UNUSED_ARG(owner);
@@ -287,10 +282,10 @@ static int read_mage(attrib * a, void *owner, struct gamedata *data)
 }
 
 static void
-write_mage(const attrib * a, const void *owner, struct storage *store)
+write_mage(const variant *var, const void *owner, struct storage *store)
 {
     int i;
-    sc_mage *mage = (sc_mage *)a->data.v;
+    sc_mage *mage = (sc_mage *)var->v;
 
     UNUSED_ARG(owner);
     WRITE_INT(store, mage->magietyp);
@@ -2090,9 +2085,9 @@ bool is_familiar(const unit * u)
 }
 
 static void
-a_write_unit(const attrib * a, const void *owner, struct storage *store)
+a_write_unit(const variant *var, const void *owner, struct storage *store)
 {
-    unit *u = (unit *)a->data.v;
+    unit *u = (unit *)var->v;
     UNUSED_ARG(owner);
     write_unit_reference(u, store);
 }
@@ -2208,10 +2203,10 @@ static void * resolve_familiar(int id, void *data) {
     return data;
 }
 
-static int read_familiar(attrib * a, void *owner, struct gamedata *data)
+static int read_familiar(variant *var, void *owner, struct gamedata *data)
 {
     UNUSED_ARG(owner);
-    if (read_unit_reference(data, (unit **)&a->data.v, resolve_familiar) <= 0) {
+    if (read_unit_reference(data, (unit **)&var->v, resolve_familiar) <= 0) {
         return AT_READ_FAIL;
     }
     return AT_READ_OK;
@@ -2289,10 +2284,10 @@ static void * resolve_clone(int id, void *data) {
     return data;
 }
 
-static int read_clone(attrib * a, void *owner, struct gamedata *data)
+static int read_clone(variant *var, void *owner, struct gamedata *data)
 {
     UNUSED_ARG(owner);
-    if (read_unit_reference(data, (unit **)&a->data.v, resolve_clone) <= 0) {
+    if (read_unit_reference(data, (unit **)&var->v, resolve_clone) <= 0) {
         return AT_READ_FAIL;
     }
     return AT_READ_OK;
@@ -2312,10 +2307,10 @@ static void * resolve_mage(int id, void *data) {
     return data;
 }
 
-static int read_magician(attrib * a, void *owner, struct gamedata *data)
+static int read_magician(variant *var, void *owner, struct gamedata *data)
 {
     UNUSED_ARG(owner);
-    if (read_unit_reference(data, (unit **)&a->data.v, resolve_mage) <= 0) {
+    if (read_unit_reference(data, (unit **)&var->v, resolve_mage) <= 0) {
         return AT_READ_FAIL;
     }
     return AT_READ_OK;
diff --git a/src/market.c b/src/market.c
index fd62ae353..87be18f95 100644
--- a/src/market.c
+++ b/src/market.c
@@ -53,11 +53,11 @@ static unsigned int get_markets(region * r, unit ** results, size_t size)
     return n;
 }
 
-static void free_market(attrib * a)
+static void free_market(variant *var)
 {
-    item *items = (item *)a->data.v;
+    item *items = (item *)var->v;
     i_freeall(&items);
-    a->data.v = 0;
+    var->v = NULL;
 }
 
 attrib_type at_market = {
diff --git a/src/modules/gmcmd.c b/src/modules/gmcmd.c
index c911e3de4..401879cd6 100644
--- a/src/modules/gmcmd.c
+++ b/src/modules/gmcmd.c
@@ -34,6 +34,7 @@
 /* util includes */
 #include <util/attrib.h>
 #include <util/gamedata.h>
+#include <util/macros.h>
 
 #include <storage.h>
 
@@ -43,17 +44,19 @@
 #include <limits.h>
 #include <assert.h>
 
-static int read_permissions(attrib * a, void *owner, struct gamedata *data)
+static int read_permissions(variant *var, void *owner, struct gamedata *data)
 {
-    assert(!a);
+    attrib *a;
+    UNUSED_ARG(var);
     read_attribs(data, &a, owner);
     a_remove(&a, a);
     return AT_READ_OK;
 }
 
-static int read_gmcreate(attrib * a, void *owner, struct gamedata *data)
+static int read_gmcreate(variant *var, void *owner, struct gamedata *data)
 {
     char zText[32];
+    UNUSED_ARG(var);
     READ_TOK(data->store, zText, sizeof(zText));
     return AT_READ_OK;
 }
diff --git a/src/modules/museum.c b/src/modules/museum.c
index 0f9fe9923..d854d1763 100644
--- a/src/modules/museum.c
+++ b/src/modules/museum.c
@@ -59,29 +59,22 @@ attrib_type at_museumexit = {
     "museumexit", NULL, NULL, NULL, a_writeshorts, a_readshorts
 };
 
-static void a_initmuseumgivebackcookie(attrib * a)
+static void a_initmuseumgivebackcookie(variant *var)
 {
-    a->data.v = calloc(1, sizeof(museumgivebackcookie));
+    var->v = calloc(1, sizeof(museumgivebackcookie));
 }
 
-static void a_finalizemuseumgivebackcookie(attrib * a)
+static void a_writemuseumgivebackcookie(const variant *var,
+    const void *owner, struct storage *store)
 {
-    free(a->data.v);
-}
-
-static void
-a_writemuseumgivebackcookie(const attrib * a, const void *owner,
-struct storage *store)
-{
-    museumgivebackcookie *gbc = (museumgivebackcookie *)a->data.v;
+    museumgivebackcookie *gbc = (museumgivebackcookie *)var->v;
     WRITE_INT(store, gbc->warden_no);
     WRITE_INT(store, gbc->cookie);
 }
 
-static int
-a_readmuseumgivebackcookie(attrib * a, void *owner, gamedata *data)
+static int a_readmuseumgivebackcookie(variant *var, void *owner, gamedata *data)
 {
-    museumgivebackcookie *gbc = (museumgivebackcookie *)a->data.v;
+    museumgivebackcookie *gbc = (museumgivebackcookie *)var->v;
     READ_INT(data->store, &gbc->warden_no);
     READ_INT(data->store, &gbc->cookie);
     return AT_READ_OK;
@@ -90,7 +83,7 @@ a_readmuseumgivebackcookie(attrib * a, void *owner, gamedata *data)
 attrib_type at_museumgivebackcookie = {
     "museumgivebackcookie",
     a_initmuseumgivebackcookie,
-    a_finalizemuseumgivebackcookie,
+    a_free_voidptr,
     NULL,
     a_writemuseumgivebackcookie,
     a_readmuseumgivebackcookie
@@ -100,30 +93,30 @@ attrib_type at_warden = {
     "itemwarden", NULL, NULL, NULL, a_writeint, a_readint
 };
 
-static void a_initmuseumgiveback(attrib * a)
+static void a_initmuseumgiveback(variant *var)
 {
-    a->data.v = calloc(1, sizeof(museumgiveback));
+    var->v = calloc(1, sizeof(museumgiveback));
 }
 
-static void a_finalizemuseumgiveback(attrib * a)
+static void a_finalizemuseumgiveback(variant *var)
 {
-    museumgiveback *gb = (museumgiveback *)a->data.v;
+    museumgiveback *gb = (museumgiveback *)var->v;
     i_freeall(&gb->items);
-    free(a->data.v);
+    free(gb);
 }
 
 static void
-a_writemuseumgiveback(const attrib * a, const void *owner,
+a_writemuseumgiveback(const variant *var, const void *owner,
 struct storage *store)
 {
-    museumgiveback *gb = (museumgiveback *)a->data.v;
+    museumgiveback *gb = (museumgiveback *)var->v;
     WRITE_INT(store, gb->cookie);
     write_items(store, gb->items);
 }
 
-static int a_readmuseumgiveback(attrib * a, void *owner, struct gamedata *data)
+static int a_readmuseumgiveback(variant *var, void *owner, struct gamedata *data)
 {
-    museumgiveback *gb = (museumgiveback *)a->data.v;
+    museumgiveback *gb = (museumgiveback *)var->v;
     READ_INT(data->store, &gb->cookie);
     read_items(data->store, &gb->items);
     return AT_READ_OK;
diff --git a/src/move.c b/src/move.c
index 069c6ff7d..3a007d26d 100644
--- a/src/move.c
+++ b/src/move.c
@@ -138,14 +138,9 @@ get_followers(unit * target, region * r, const region_list * route_end,
     }
 }
 
-static void shiptrail_init(attrib * a)
+static void shiptrail_init(variant *var)
 {
-    a->data.v = calloc(1, sizeof(traveldir));
-}
-
-static void shiptrail_finalize(attrib * a)
-{
-    free(a->data.v);
+    var->v = calloc(1, sizeof(traveldir));
 }
 
 static int shiptrail_age(attrib * a, void *owner)
@@ -157,11 +152,11 @@ static int shiptrail_age(attrib * a, void *owner)
     return (t->age > 0) ? AT_AGE_KEEP : AT_AGE_REMOVE;
 }
 
-static int shiptrail_read(attrib * a, void *owner, struct gamedata *data)
+static int shiptrail_read(variant *var, void *owner, struct gamedata *data)
 {
     storage *store = data->store;
     int n;
-    traveldir *t = (traveldir *)(a->data.v);
+    traveldir *t = (traveldir *)var->v;
 
     UNUSED_ARG(owner);
     READ_INT(store, &t->no);
@@ -172,9 +167,9 @@ static int shiptrail_read(attrib * a, void *owner, struct gamedata *data)
 }
 
 static void
-shiptrail_write(const attrib * a, const void *owner, struct storage *store)
+shiptrail_write(const variant *var, const void *owner, struct storage *store)
 {
-    traveldir *t = (traveldir *)(a->data.v);
+    traveldir *t = (traveldir *)var->v;
 
     UNUSED_ARG(owner);
     WRITE_INT(store, t->no);
@@ -185,7 +180,7 @@ shiptrail_write(const attrib * a, const void *owner, struct storage *store)
 attrib_type at_shiptrail = {
     "traveldir_new",
     shiptrail_init,
-    shiptrail_finalize,
+    a_free_voidptr,
     shiptrail_age,
     shiptrail_write,
     shiptrail_read
diff --git a/src/piracy.c b/src/piracy.c
index 888a7e53a..da0e4a3e1 100644
--- a/src/piracy.c
+++ b/src/piracy.c
@@ -34,20 +34,15 @@ typedef struct piracy_data {
     direction_t dir;
 } piracy_data;
 
-static void piracy_init(struct attrib *a)
+static void piracy_init(variant *var)
 {
-    a->data.v = calloc(1, sizeof(piracy_data));
-}
-
-static void piracy_done(struct attrib *a)
-{
-    free(a->data.v);
+    var->v = calloc(1, sizeof(piracy_data));
 }
 
 static attrib_type at_piracy_direction = {
     "piracy_direction",
     piracy_init,
-    piracy_done,
+    a_free_voidptr,
     DEFAULT_AGE,
     NO_WRITE,
     NO_READ
diff --git a/src/spells.c b/src/spells.c
index 8334d19c0..12778e9d4 100644
--- a/src/spells.c
+++ b/src/spells.c
@@ -2857,12 +2857,12 @@ static curse *mk_deathcloud(unit * mage, region * r, double force, int duration)
     return c;
 }
 
-static int dc_read_compat(struct attrib *a, void *target, gamedata *data)
+static int dc_read_compat(variant *var, void *target, gamedata *data)
 /* return AT_READ_OK on success, AT_READ_FAIL if attrib needs removal */
 {
     struct storage *store = data->store;
 
-    UNUSED_ARG(a);
+    UNUSED_ARG(var);
     UNUSED_ARG(target);
     READ_INT(store, NULL);
     READ_FLT(store, NULL);
diff --git a/src/spells/borders.c b/src/spells/borders.c
index 9ded973c8..c30726641 100644
--- a/src/spells/borders.c
+++ b/src/spells/borders.c
@@ -31,13 +31,13 @@ typedef struct wallcurse {
     connection *wall;
 } wallcurse;
 
-static int cw_read_depr(attrib * a, void *target, gamedata *data)
+static int cw_read_depr(variant *var, void *target, gamedata *data)
 {
     storage *store = data->store;
 
-    curse_init(a);
-    curse_read(a, store, target);
-    curse_done(a);
+    curse_init(var);
+    curse_read(var, store, target);
+    curse_done(var);
     READ_INT(store, NULL);
     return AT_READ_DEPR;
 }
diff --git a/src/study.c b/src/study.c
index 09f228f45..d9e66f1f9 100644
--- a/src/study.c
+++ b/src/study.c
@@ -173,16 +173,16 @@ int study_cost(struct unit *u, skill_t sk)
 
 /* ------------------------------------------------------------- */
 
-static void init_learning(struct attrib *a)
+static void init_learning(variant *var)
 {
-    a->data.v = calloc(sizeof(teaching_info), 1);
+    var->v = calloc(sizeof(teaching_info), 1);
 }
 
-static void done_learning(struct attrib *a)
+static void done_learning(variant *var)
 {
-    teaching_info *teach = (teaching_info *)a->data.v;
+    teaching_info *teach = (teaching_info *)var->v;
     selist_free(teach->teachers);
-    free(a->data.v);
+    free(teach);
 }
 
 const attrib_type at_learning = {
diff --git a/src/travelthru.c b/src/travelthru.c
index 835d05024..fa6293f53 100644
--- a/src/travelthru.c
+++ b/src/travelthru.c
@@ -37,9 +37,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include <assert.h>
 #include <string.h>
 
-static void travel_done(attrib *a) {
-    selist *ql = (selist *)a->data.v;
-    selist_free(ql);
+static void travel_done(variant *var) {
+    selist_free((selist *)var->v);
 }
 
 /*********************/
diff --git a/src/util/attrib.c b/src/util/attrib.c
index 8a90f3bca..f65972fc0 100644
--- a/src/util/attrib.c
+++ b/src/util/attrib.c
@@ -21,6 +21,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 #include "gamedata.h"
 #include "log.h"
+#include "variant.h"
 #include "storage.h"
 #include "strings.h"
 
@@ -61,57 +62,57 @@ void write_attribs(storage *store, attrib *alist, const void *owner)
 #endif
 }
 
-int a_readint(attrib * a, void *owner, struct gamedata *data)
+int a_readint(variant * var, void *owner, struct gamedata *data)
 {
     int n;
     READ_INT(data->store, &n);
-    if (a) a->data.i = n;
+    if (var) var->i = n;
     return AT_READ_OK;
 }
 
-void a_writeint(const attrib * a, const void *owner, struct storage *store)
+void a_writeint(const variant * var, const void *owner, struct storage *store)
 {
-    WRITE_INT(store, a->data.i);
+    WRITE_INT(store, var->i);
 }
 
-int a_readshorts(attrib * a, void *owner, struct gamedata *data)
+int a_readshorts(variant * var, void *owner, struct gamedata *data)
 {
     int n;
     READ_INT(data->store, &n);
-    a->data.sa[0] = (short)n;
+    var->sa[0] = (short)n;
     READ_INT(data->store, &n);
-    a->data.sa[1] = (short)n;
+    var->sa[1] = (short)n;
     return AT_READ_OK;
 }
 
-void a_writeshorts(const attrib * a, const void *owner, struct storage *store)
+void a_writeshorts(const variant * var, const void *owner, struct storage *store)
 {
-    WRITE_INT(store, a->data.sa[0]);
-    WRITE_INT(store, a->data.sa[1]);
+    WRITE_INT(store, var->sa[0]);
+    WRITE_INT(store, var->sa[1]);
 }
 
-int a_readchars(attrib * a, void *owner, struct gamedata *data)
+int a_readchars(variant * var, void *owner, struct gamedata *data)
 {
     int i;
     for (i = 0; i != 4; ++i) {
         int n;
         READ_INT(data->store, &n);
-        a->data.ca[i] = (char)n;
+        var->ca[i] = (char)n;
     }
     return AT_READ_OK;
 }
 
-void a_writechars(const attrib * a, const void *owner, struct storage *store)
+void a_writechars(const variant * var, const void *owner, struct storage *store)
 {
     int i;
 
     for (i = 0; i != 4; ++i) {
-        WRITE_INT(store, a->data.ca[i]);
+        WRITE_INT(store, var->ca[i]);
     }
 }
 
 #define DISPLAYSIZE 8192
-int a_readstring(attrib * a, void *owner, struct gamedata *data)
+int a_readstring(variant * var, void *owner, struct gamedata *data)
 {
     char buf[DISPLAYSIZE];
     char * result = 0;
@@ -133,19 +134,19 @@ int a_readstring(attrib * a, void *owner, struct gamedata *data)
             result = str_strdup(buf);
         }
     } while (e == ENOMEM);
-    a->data.v = result;
+    var->v = result;
     return AT_READ_OK;
 }
 
-void a_writestring(const attrib * a, const void *owner, struct storage *store)
+void a_writestring(const variant * var, const void *owner, struct storage *store)
 {
-    assert(a->data.v);
-    WRITE_STR(store, (const char *)a->data.v);
+    assert(var && var->v);
+    WRITE_STR(store, (const char *)var->v);
 }
 
-void a_finalizestring(attrib * a)
+void a_finalizestring(variant * var)
 {
-    free(a->data.v);
+    free(var->v);
 }
 
 #define MAXATHASH 61
@@ -308,11 +309,16 @@ static int a_unlink(attrib ** pa, attrib * a)
     return 0;
 }
 
+void a_free_voidptr(union variant *var)
+{
+    free(var->v);
+}
+
 static void a_free(attrib * a)
 {
     const attrib_type *at = a->type;
     if (at->finalize)
-        at->finalize(a);
+        at->finalize(&a->data);
     free(a);
 }
 
@@ -376,7 +382,7 @@ attrib *a_new(const attrib_type * at)
     assert(at != NULL);
     a->type = at;
     if (at->initialize)
-        at->initialize(a);
+        at->initialize(&a->data);
     return a;
 }
 
@@ -404,10 +410,10 @@ static critbit_tree cb_deprecated = { 0 };
 
 typedef struct deprecated_s {
     unsigned int hash;
-    int(*reader)(attrib *, void *, struct gamedata *);
+    int(*reader)(variant *, void *, struct gamedata *);
 } deprecated_t;
 
-void at_deprecate(const char * name, int(*reader)(attrib *, void *, struct gamedata *))
+void at_deprecate(const char * name, int(*reader)(variant *, void *, struct gamedata *))
 {
     deprecated_t value;
     
@@ -418,7 +424,7 @@ void at_deprecate(const char * name, int(*reader)(attrib *, void *, struct gamed
 
 static int a_read_i(gamedata *data, attrib ** attribs, void *owner, unsigned int key) {
     int retval = AT_READ_OK;
-    int(*reader)(attrib *, void *, struct gamedata *) = 0;
+    int(*reader)(variant *, void *, struct gamedata *) = 0;
     attrib_type *at = at_find_key(key);
     attrib * na = 0;
 
@@ -438,7 +444,7 @@ static int a_read_i(gamedata *data, attrib ** attribs, void *owner, unsigned int
         }
     }
     if (reader) {
-        int ret = reader(na, owner, data);
+        int ret = reader(&na->data, owner, data);
         if (na) {
             switch (ret) {
             case AT_READ_DEPR:
@@ -508,7 +514,7 @@ void a_write(struct storage *store, const attrib * attribs, const void *owner) {
         if (na->type->write) {
             assert(na->type->hashkey || !"attribute not registered");
             WRITE_INT(store, na->type->hashkey);
-            na->type->write(na, owner, store);
+            na->type->write(&na->data, owner, store);
             na = na->next;
         }
         else {
@@ -526,7 +532,7 @@ void a_write_orig(struct storage *store, const attrib * attribs, const void *own
         if (na->type->write) {
             assert(na->type->hashkey || !"attribute not registered");
             WRITE_TOK(store, na->type->name);
-            na->type->write(na, owner, store);
+            na->type->write(&na->data, owner, store);
             na = na->next;
         }
         else {
diff --git a/src/util/attrib.h b/src/util/attrib.h
index 6043a960f..434c60013 100644
--- a/src/util/attrib.h
+++ b/src/util/attrib.h
@@ -20,27 +20,19 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #define ATTRIB_H
 
 #include <stdbool.h>
-
+#include "variant.h"
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+    union variant;
     struct gamedata;
     struct storage;
     typedef void(*afun) (void);
 
     typedef struct attrib {
         const struct attrib_type *type;
-        union {
-            afun f;
-            void *v;
-            int i;
-            float flt;
-            char c;
-            short s;
-            short sa[2];
-            char ca[4];
-        } data;
+        union variant data;
         /* internal data, do not modify: */
         struct attrib *next;        /* next attribute in the list */
         struct attrib *nexttype;    /* skip to attribute of a different type */
@@ -52,12 +44,12 @@ extern "C" {
 
     typedef struct attrib_type {
         const char *name;
-        void(*initialize) (struct attrib *);
-        void(*finalize) (struct attrib *);
+        void(*initialize) (union variant *);
+        void(*finalize) (union variant *);
         int(*age) (struct attrib *, void *owner);
         /* age returns 0 if the attribute needs to be removed, !=0 otherwise */
-        void(*write) (const struct attrib *, const void *owner, struct storage *);
-        int(*read) (struct attrib *, void *owner, struct gamedata *);       /* return AT_READ_OK on success, AT_READ_FAIL if attrib needs removal */
+        void(*write) (const union variant *, const void *owner, struct storage *);
+        int(*read) (union variant *, void *owner, struct gamedata *);       /* return AT_READ_OK on success, AT_READ_FAIL if attrib needs removal */
         void(*upgrade) (struct attrib **alist, struct attrib *a);
         unsigned int flags;
         /* ---- internal data, do not modify: ---- */
@@ -66,7 +58,7 @@ extern "C" {
     } attrib_type;
 
     void at_register(attrib_type * at);
-    void at_deprecate(const char * name, int(*reader)(attrib *, void *, struct gamedata *));
+    void at_deprecate(const char * name, int (*reader)(variant *, void *, struct gamedata *));
     struct attrib_type *at_find(const char *name);
 
     void write_attribs(struct storage *store, struct attrib *alist, const void *owner);
@@ -80,25 +72,26 @@ extern "C" {
     attrib *a_new(const attrib_type * at);
     int a_age(attrib ** attribs, void *owner);
 
+    void a_free_voidptr(union variant *v);
     int a_read_orig(struct gamedata *data, attrib ** attribs, void *owner);
     void a_write_orig(struct storage *store, const attrib * attribs, const void *owner);
 
     int a_read(struct gamedata *data, attrib ** attribs, void *owner);
     void a_write(struct storage *store, const attrib * attribs, const void *owner);
 
-    int a_readint(struct attrib *a, void *owner, struct gamedata *);
-    void a_writeint(const struct attrib *a, const void *owner,
+    int a_readint(union variant *v, void *owner, struct gamedata *);
+    void a_writeint(const union variant *v, const void *owner,
         struct storage *store);
-    int a_readshorts(struct attrib *a, void *owner, struct gamedata *);
-    void a_writeshorts(const struct attrib *a, const void *owner,
+    int a_readshorts(union variant *v, void *owner, struct gamedata *);
+    void a_writeshorts(const union variant *v, const void *owner,
         struct storage *store);
-    int a_readchars(struct attrib *a, void *owner, struct gamedata *);
-    void a_writechars(const struct attrib *a, const void *owner,
+    int a_readchars(union variant *v, void *owner, struct gamedata *);
+    void a_writechars(const union variant *v, const void *owner,
         struct storage *store);
-    int a_readstring(struct attrib *a, void *owner, struct gamedata *);
-    void a_writestring(const struct attrib *a, const void *owner,
+    int a_readstring(union variant *v, void *owner, struct gamedata *);
+    void a_writestring(const union variant *v, const void *owner,
         struct storage *);
-    void a_finalizestring(struct attrib *a);
+    void a_finalizestring(union variant *v);
 
     void attrib_done(void);
 
diff --git a/src/util/attrib.test.c b/src/util/attrib.test.c
index 34427385f..d266196e6 100644
--- a/src/util/attrib.test.c
+++ b/src/util/attrib.test.c
@@ -117,18 +117,18 @@ static void test_attrib_nexttype(CuTest * tc)
 static void test_attrib_rwstring(CuTest *tc) {
     gamedata data;
     storage store;
-    attrib a = { 0 };
+    variant var = { 0 };
 
     test_setup();
-    a.data.v = str_strdup("Hello World");
+    var.v = str_strdup("Hello World");
     mstream_init(&data.strm);
     gamedata_init(&data, &store, RELEASE_VERSION);
-    a_writestring(&a, NULL, &store);
-    a_finalizestring(&a);
+    a_writestring(&var, NULL, &store);
+    a_finalizestring(&var);
     data.strm.api->rewind(data.strm.handle);
-    a_readstring(&a, NULL, &data);
-    CuAssertStrEquals(tc, "Hello World", (const char *)a.data.v);
-    a_finalizestring(&a);
+    a_readstring(&var, NULL, &data);
+    CuAssertStrEquals(tc, "Hello World", (const char *)var.v);
+    a_finalizestring(&var);
     mstream_done(&data.strm);
     gamedata_done(&data);
     test_teardown();
@@ -137,17 +137,17 @@ static void test_attrib_rwstring(CuTest *tc) {
 static void test_attrib_rwint(CuTest *tc) {
     gamedata data;
     storage store;
-    attrib a = { 0 };
+    variant var = { 0 };
 
     test_setup();
-    a.data.i = 42;
+    var.i = 42;
     mstream_init(&data.strm);
     gamedata_init(&data, &store, RELEASE_VERSION);
-    a_writeint(&a, NULL, &store);
-    a.data.i = 0;
+    a_writeint(&var, NULL, &store);
+    var.i = 0;
     data.strm.api->rewind(data.strm.handle);
-    a_readint(&a, NULL, &data);
-    CuAssertIntEquals(tc, 42, a.data.i);
+    a_readint(&var, NULL, &data);
+    CuAssertIntEquals(tc, 42, var.i);
     mstream_done(&data.strm);
     gamedata_done(&data);
     test_teardown();
@@ -156,19 +156,19 @@ static void test_attrib_rwint(CuTest *tc) {
 static void test_attrib_rwchars(CuTest *tc) {
     gamedata data;
     storage store;
-    attrib a = { 0 };
+    variant var = { 0 };
 
     test_setup();
-    a.data.ca[0] = 1;
-    a.data.ca[3] = 42;
+    var.ca[0] = 1;
+    var.ca[3] = 42;
     mstream_init(&data.strm);
     gamedata_init(&data, &store, RELEASE_VERSION);
-    a_writeint(&a, NULL, &store);
-    memset(a.data.ca, 0, 4);
+    a_writeint(&var, NULL, &store);
+    memset(var.ca, 0, 4);
     data.strm.api->rewind(data.strm.handle);
-    a_readint(&a, NULL, &data);
-    CuAssertIntEquals(tc, 1, a.data.ca[0]);
-    CuAssertIntEquals(tc, 42, a.data.ca[3]);
+    a_readint(&var, NULL, &data);
+    CuAssertIntEquals(tc, 1, var.ca[0]);
+    CuAssertIntEquals(tc, 42, var.ca[3]);
     mstream_done(&data.strm);
     gamedata_done(&data);
     test_teardown();
@@ -177,19 +177,19 @@ static void test_attrib_rwchars(CuTest *tc) {
 static void test_attrib_rwshorts(CuTest *tc) {
     gamedata data;
     storage store;
-    attrib a = { 0 };
-    a.data.sa[0] = -4;
-    a.data.sa[1] = 42;
+    variant var = { 0 };
+    var.sa[0] = -4;
+    var.sa[1] = 42;
 
     test_setup();
     mstream_init(&data.strm);
     gamedata_init(&data, &store, RELEASE_VERSION);
-    a_writeint(&a, NULL, &store);
-    memset(a.data.ca, 0, 4);
+    a_writeint(&var, NULL, &store);
+    memset(var.ca, 0, 4);
     data.strm.api->rewind(data.strm.handle);
-    a_readint(&a, NULL, &data);
-    CuAssertIntEquals(tc, -4, a.data.sa[0]);
-    CuAssertIntEquals(tc, 42, a.data.sa[1]);
+    a_readint(&var, NULL, &data);
+    CuAssertIntEquals(tc, -4, var.sa[0]);
+    CuAssertIntEquals(tc, 42, var.sa[1]);
     mstream_done(&data.strm);
     gamedata_done(&data);
     test_teardown();
diff --git a/src/util/event.c b/src/util/event.c
index 298f9d93e..d6aa6f338 100644
--- a/src/util/event.c
+++ b/src/util/event.c
@@ -127,32 +127,32 @@ typedef struct handler_info {
     trigger *triggers;
 } handler_info;
 
-static void init_handler(attrib * a)
+static void init_handler(variant *var)
 {
-    a->data.v = calloc(sizeof(handler_info), 1);
+    var->v = calloc(sizeof(handler_info), 1);
 }
 
-static void free_handler(attrib * a)
+static void free_handler(variant *var)
 {
-    handler_info *hi = (handler_info *)a->data.v;
+    handler_info *hi = (handler_info *)var->v;
     free_triggers(hi->triggers);
     free(hi->event);
     free(hi);
 }
 
 static void
-write_handler(const attrib * a, const void *owner, struct storage *store)
+write_handler(const variant *var, const void *owner, struct storage *store)
 {
-    handler_info *hi = (handler_info *)a->data.v;
+    handler_info *hi = (handler_info *)var->v;
     WRITE_TOK(store, hi->event);
     write_triggers(store, hi->triggers);
 }
 
-static int read_handler(attrib * a, void *owner, gamedata *data)
+static int read_handler(variant *var, void *owner, gamedata *data)
 {
     struct storage *store = data->store;
     char zText[128];
-    handler_info *hi = (handler_info *)a->data.v;
+    handler_info *hi = (handler_info *)var->v;
 
     READ_TOK(store, zText, sizeof(zText));
     hi->event = str_strdup(zText);
diff --git a/src/vortex.c b/src/vortex.c
index bb30524ef..69d767c1b 100644
--- a/src/vortex.c
+++ b/src/vortex.c
@@ -65,14 +65,14 @@ void register_special_direction(struct locale *lang, const char *name)
 /********************/
 /*   at_direction   */
 /********************/
-static void a_initdirection(attrib * a)
+static void a_initdirection(variant * var)
 {
-    a->data.v = calloc(1, sizeof(spec_direction));
+    var->v = calloc(1, sizeof(spec_direction));
 }
 
-static void a_freedirection(attrib * a)
+static void a_freedirection(variant *var)
 {
-    spec_direction *d = (spec_direction *)(a->data.v);
+    spec_direction *d = (spec_direction *)(var->v);
     free(d->desc);
     free(d->keyword);
     free(d);
@@ -86,10 +86,10 @@ static int a_agedirection(attrib * a, void *owner)
     return (d->duration > 0) ? AT_AGE_KEEP : AT_AGE_REMOVE;
 }
 
-static int a_readdirection(attrib * a, void *owner, struct gamedata *data)
+static int a_readdirection(variant *var, void *owner, struct gamedata *data)
 {
     struct storage *store = data->store;
-    spec_direction *d = (spec_direction *)(a->data.v);
+    spec_direction *d = (spec_direction *)(var->v);
     char lbuf[32];
 
     (void)owner;
@@ -105,9 +105,9 @@ static int a_readdirection(attrib * a, void *owner, struct gamedata *data)
 }
 
 static void
-a_writedirection(const attrib * a, const void *owner, struct storage *store)
+a_writedirection(const variant *var, const void *owner, struct storage *store)
 {
-    spec_direction *d = (spec_direction *)(a->data.v);
+    spec_direction *d = (spec_direction *)(var->v);
 
     (void)owner;
     WRITE_INT(store, d->x);
diff --git a/src/wormhole.c b/src/wormhole.c
index 2366a7e9c..197fc346d 100644
--- a/src/wormhole.c
+++ b/src/wormhole.c
@@ -91,20 +91,20 @@ static int wormhole_age(struct attrib *a, void *owner)
     return AT_AGE_KEEP;
 }
 
-static void wormhole_write(const struct attrib *a, const void *owner, struct storage *store)
+static void wormhole_write(const variant *var, const void *owner, struct storage *store)
 {
-    region *exit = (region *)a->data.v;
+    region *exit = (region *)var->v;
     write_region_reference(exit, store);
 }
 
-static int wormhole_read(struct attrib *a, void *owner, struct gamedata *data)
+static int wormhole_read(variant *var, void *owner, struct gamedata *data)
 {
     int id;
 
     if (data->version < ATTRIBOWNER_VERSION) {
         READ_INT(data->store, NULL);
     }
-    id = read_region_reference(data, (region **)&a->data.v, NULL);
+    id = read_region_reference(data, (region **)&var->v, NULL);
     return (id <= 0) ? AT_READ_FAIL : AT_READ_OK;
 }
 

From c3fce574fbbe91da7283a47403a5ea04af9e7bec Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sat, 10 Feb 2018 11:26:39 +0100
Subject: [PATCH 09/40] fix reading old data files.

---
 src/util/attrib.c | 23 +++++++++++++++--------
 1 file changed, 15 insertions(+), 8 deletions(-)

diff --git a/src/util/attrib.c b/src/util/attrib.c
index f65972fc0..9530e5154 100644
--- a/src/util/attrib.c
+++ b/src/util/attrib.c
@@ -42,12 +42,13 @@ int read_attribs(gamedata *data, attrib **alist, void *owner) {
     }
     if (result == AT_READ_DEPR) {
         /* handle deprecated attributes */
-        attrib *a = *alist;
-        while (a) {
+        attrib **ap = alist;
+        while (*ap) {
+            attrib *a = *ap;
             if (a->type->upgrade) {
                 a->type->upgrade(alist, a);
             }
-            a = a->nexttype;
+            ap = &a->nexttype;
         }
     }
     return result;
@@ -66,7 +67,7 @@ int a_readint(variant * var, void *owner, struct gamedata *data)
 {
     int n;
     READ_INT(data->store, &n);
-    if (var) var->i = n;
+    var->i = n;
     return AT_READ_OK;
 }
 
@@ -426,11 +427,13 @@ static int a_read_i(gamedata *data, attrib ** attribs, void *owner, unsigned int
     int retval = AT_READ_OK;
     int(*reader)(variant *, void *, struct gamedata *) = 0;
     attrib_type *at = at_find_key(key);
-    attrib * na = 0;
+    attrib * na = NULL;
+    variant var, *vp = &var;
 
     if (at) {
         reader = at->read;
         na = a_new(at);
+        vp = &na->data;
     }
     else {
         void *match;
@@ -444,16 +447,20 @@ static int a_read_i(gamedata *data, attrib ** attribs, void *owner, unsigned int
         }
     }
     if (reader) {
-        int ret = reader(&na->data, owner, data);
+        int ret = reader(vp, owner, data);
         if (na) {
             switch (ret) {
             case AT_READ_DEPR:
             case AT_READ_OK:
-                a_add(attribs, na);
+                if (na) {
+                    a_add(attribs, na);
+                }
                 retval = ret;
                 break;
             case AT_READ_FAIL:
-                a_free(na);
+                if (na) {
+                    a_free(na);
+                }
                 break;
             default:
                 assert(!"invalid return value");

From fe29f2433ae4cdb2c2bf2e57b56c2dcfcd9c22ce Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sat, 10 Feb 2018 16:50:05 +0100
Subject: [PATCH 10/40] convert multiple seenspell attributes to single
 seenspells list. faster? maybe.

---
 src/attributes/attributes.c |   2 +-
 src/attributes/seenspell.c  | 109 ++++++++++++++++++++++++++++--------
 src/attributes/seenspell.h  |   3 +-
 3 files changed, 90 insertions(+), 24 deletions(-)

diff --git a/src/attributes/attributes.c b/src/attributes/attributes.c
index e6887a777..cfa99fcfd 100644
--- a/src/attributes/attributes.c
+++ b/src/attributes/attributes.c
@@ -179,8 +179,8 @@ void register_attributes(void)
     at_register(&at_mage);
     at_register(&at_countdown);
     at_register(&at_curse);
-
     at_register(&at_seenspell);
+    at_register(&at_seenspells);
 
     /* neue REGION-Attribute */
     at_register(&at_moveblock);
diff --git a/src/attributes/seenspell.c b/src/attributes/seenspell.c
index e7fd2e38a..3d813ee45 100644
--- a/src/attributes/seenspell.c
+++ b/src/attributes/seenspell.c
@@ -34,6 +34,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include <storage.h>
 
 #include <stdlib.h>
+#include <string.h>
 
 /* ------------------------------------------------------------- */
 /* Ausgabe der Spruchbeschreibungen
@@ -43,6 +44,43 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  * Spruch zu seiner List-of-known-spells hinzugefügt werden.
  */
 
+static int read_seenspells(variant *var, void *owner, struct gamedata *data)
+{
+    selist *ql;
+    storage *store = data->store;
+    spell *sp = 0;
+    char token[32];
+
+    UNUSED_ARG(owner);
+    READ_TOK(store, token, sizeof(token));
+    while (token[0]) {
+        sp = find_spell(token);
+        if (!sp) {
+            log_info("read_seenspells: could not find spell '%s'\n", token);
+            return AT_READ_FAIL;
+        }
+        selist_push(&ql, sp);
+        READ_TOK(store, token, sizeof(token));
+    }
+    var->v = ql;
+    return AT_READ_OK;
+}
+
+static bool cb_write_spell(const void *data, void *more) {
+    const spell *sp = (const spell *)data;
+    storage *store = (storage *)more;
+    WRITE_TOK(store, sp->sname);
+    return true;
+
+}
+static void
+write_seenspells(const variant *var, const void *owner, struct storage *store)
+{
+    UNUSED_ARG(owner);
+    selist_foreach_ex((selist *)var->v, cb_write_spell, store);
+    WRITE_TOK(store, "");
+}
+
 static int read_seenspell(variant *var, void *owner, struct gamedata *data)
 {
     storage *store = data->store;
@@ -60,7 +98,7 @@ static int read_seenspell(variant *var, void *owner, struct gamedata *data)
         return AT_READ_FAIL;
     }
     var->v = sp;
-    return AT_READ_OK;
+    return AT_READ_DEPR;
 }
 
 static void
@@ -71,18 +109,53 @@ write_seenspell(const variant *var, const void *owner, struct storage *store)
     WRITE_TOK(store, sp->sname);
 }
 
+static int cmp_spell(const void *a, const void *b) {
+    const spell *spa = (const spell *)a;
+    const spell *spb = (const spell *)b;
+    return strcmp(spa->sname, spb->sname);
+}
+
+static bool set_seen(attrib **alist, struct spell *sp) {
+    attrib *a = a_find(*alist, &at_seenspells);
+    selist *sl;
+    if (!a) {
+        a = a_add(alist, a_new(&at_seenspells));
+    }
+    sl = (selist *)a->data.v;
+    return selist_set_insert(&sl, sp, cmp_spell);
+}
+
+static void upgrade_seenspell(attrib **alist, attrib *abegin) {
+    attrib *a, *ak;
+
+    ak = a_find(*alist, &at_seenspells);
+    if (ak) alist = &ak;
+    for (a = abegin; a && a->type == abegin->type; a = a->next) {
+        set_seen(alist, (struct spell *)a->data.v);
+    }
+}
+
+static void free_seenspells(variant *var) {
+    selist *sl = (selist *)var->v;
+    selist_free(sl);
+}
+
+attrib_type at_seenspells = {
+    "seenspells", NULL, free_seenspells, NULL, write_seenspells, read_seenspells
+};
+
 attrib_type at_seenspell = {
-    "seenspell", NULL, NULL, NULL, write_seenspell, read_seenspell
+    "seenspell", NULL, NULL, NULL, NULL, read_seenspell, upgrade_seenspell
 };
 
 static bool already_seen(const faction * f, const spell * sp)
 {
     attrib *a;
 
-    for (a = a_find(f->attribs, &at_seenspell); a && a->type == &at_seenspell;
-        a = a->next) {
-        if (a->data.v == sp)
-            return true;
+    a = a_find(f->attribs, &at_seenspells);
+    if (a) {
+        selist *sl = (selist *)a->data.v;
+        return selist_set_find(&sl, NULL, sp, cmp_spell);
     }
     return false;
 }
@@ -94,25 +167,17 @@ attrib_type at_reportspell = {
 void show_spell(faction *f, const spellbook_entry *sbe)
 {
     if (!already_seen(f, sbe->sp)) {
-        attrib * a = a_new(&at_reportspell);
-        a->data.v = (void *)sbe;
-        a_add(&f->attribs, a);
-        a_add(&f->attribs, a_new(&at_seenspell))->data.v = sbe->sp;
+        /* mark the spell as seen by this faction: */
+        if (set_seen(&f->attribs, sbe->sp)) {
+            /* add the spell to the report: */
+            attrib * a = a_new(&at_reportspell);
+            a->data.v = (void *)sbe;
+            a_add(&f->attribs, a);
+        }
     }
 }
 
 void reset_seen_spells(faction *f, const struct spell *sp)
 {
-    if (sp) {
-        attrib *a = a_find(f->attribs, &at_seenspell);
-        while (a && a->type == &at_seenspell && a->data.v != sp) {
-            a = a->next;
-        }
-        if (a) {
-            a_remove(&f->attribs, a);
-        }
-    }
-    else {
-        a_removeall(&f->attribs, &at_seenspell);
-    }
+    a_removeall(&f->attribs, &at_seenspells);
 }
diff --git a/src/attributes/seenspell.h b/src/attributes/seenspell.h
index aada5ad0a..cdfb36e48 100644
--- a/src/attributes/seenspell.h
+++ b/src/attributes/seenspell.h
@@ -10,6 +10,7 @@ void show_spell(struct faction * f, const struct spellbook_entry *sbe);
 void reset_seen_spells(struct faction * f, const struct spell *sp);
 
 extern struct attrib_type at_reportspell;
-extern struct attrib_type at_seenspell;
+extern struct attrib_type at_seenspells;
+extern struct attrib_type at_seenspell; /* upgraded */
 
 #endif

From e300605e87b74bf11f55e1bf7c891499edbbf48f Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sat, 10 Feb 2018 17:31:48 +0100
Subject: [PATCH 11/40] fix gcc build

---
 src/attributes/seenspell.c | 10 +---------
 1 file changed, 1 insertion(+), 9 deletions(-)

diff --git a/src/attributes/seenspell.c b/src/attributes/seenspell.c
index 3d813ee45..170cc33b4 100644
--- a/src/attributes/seenspell.c
+++ b/src/attributes/seenspell.c
@@ -66,7 +66,7 @@ static int read_seenspells(variant *var, void *owner, struct gamedata *data)
     return AT_READ_OK;
 }
 
-static bool cb_write_spell(const void *data, void *more) {
+static bool cb_write_spell(void *data, void *more) {
     const spell *sp = (const spell *)data;
     storage *store = (storage *)more;
     WRITE_TOK(store, sp->sname);
@@ -101,14 +101,6 @@ static int read_seenspell(variant *var, void *owner, struct gamedata *data)
     return AT_READ_DEPR;
 }
 
-static void
-write_seenspell(const variant *var, const void *owner, struct storage *store)
-{
-    const spell *sp = (const spell *)var->v;
-    UNUSED_ARG(owner);
-    WRITE_TOK(store, sp->sname);
-}
-
 static int cmp_spell(const void *a, const void *b) {
     const spell *spa = (const spell *)a;
     const spell *spb = (const spell *)b;

From eb6271bbb809cabacc9df484c02055b301562155 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sat, 10 Feb 2018 17:53:59 +0100
Subject: [PATCH 12/40] CID 182781 I broke the wage logic.

---
 scripts/tests/e2/e2features.lua |  3 ++-
 scripts/tests/economy.lua       | 16 ++++++++++++++++
 src/kernel/building.c           |  3 ++-
 3 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/scripts/tests/e2/e2features.lua b/scripts/tests/e2/e2features.lua
index 2c9c42e5b..d9a7da4f8 100644
--- a/scripts/tests/e2/e2features.lua
+++ b/scripts/tests/e2/e2features.lua
@@ -9,6 +9,7 @@ function setup()
     eressea.settings.set("rules.ship.storms", "0")
     eressea.settings.set("rules.encounters", "0")
     eressea.settings.set("study.produceexp", "0")
+    eressea.settings.set("rules.peasants.growth.factor", "0")
 end
 
 function test_calendar()
@@ -19,7 +20,7 @@ end
 function test_herbalism()
 -- OBS: herbalism is currently an E2-only skill
     local r = region.create(0, 0, "plain")
-    local f = faction.create("human", "herbalism@eressea.de", "de")
+    local f = faction.create("human")
     local u = unit.create(f, r, 1)
 
     eressea.settings.set("rules.grow.formula", 0) -- plants do not grow
diff --git a/scripts/tests/economy.lua b/scripts/tests/economy.lua
index 719b1f764..3379b2ab7 100644
--- a/scripts/tests/economy.lua
+++ b/scripts/tests/economy.lua
@@ -9,6 +9,22 @@ function setup()
     eressea.settings.set("nmr.timeout", "0")
     eressea.settings.set("rules.food.flags", "4") -- FOOD_IS_FREE
     eressea.settings.set("rules.encounters", "0")
+    eressea.settings.set("rules.peasants.growth.factor", "0")
+end
+
+function test_work()
+    local r = region.create(0, 0, "plain")
+    r:set_resource('tree', 0)
+    r:set_resource('seed', 0)
+    r:set_resource('sapling', 0)
+    r:set_resource('peasant', 100)
+    r:set_resource('money', 0)
+    local f = faction.create("human")
+    local u = unit.create(f, r, 1)
+    u:add_order("ARBEITE")
+    process_orders()
+    assert_equal(10, u:get_item('money'))
+    assert_equal(100, r:get_resource('money'))
 end
 
 function test_bug_2361_forget_magic()
diff --git a/src/kernel/building.c b/src/kernel/building.c
index 8c5fbeb14..3bbd5891d 100644
--- a/src/kernel/building.c
+++ b/src/kernel/building.c
@@ -724,13 +724,14 @@ default_wage(const region * r, const faction * f, const race * rc, int in_turn)
     if (r->attribs) {
         attrib *a;
         curse *c;
-        variant vm = frac_make(wage, 1);
+        variant vm;
 
         /* Godcurse: Income -10 */
         c = get_curse(r->attribs, &ct_godcursezone);
         if (c && curse_active(c)) {
             wage = (wage < 10) ? 0 : (wage - 10);
         }
+        vm = frac_make(wage, 1);
 
         /* Bei einer D�rre verdient man nur noch ein Viertel  */
         c = get_curse(r->attribs, &ct_drought);

From c1c7da322dd38251e5c6faaaf1ba4e86e3fa4f65 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sat, 10 Feb 2018 18:06:02 +0100
Subject: [PATCH 13/40] Badges? We want them stinking badges!

---
 README.md | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/README.md b/README.md
index efd1318d9..7d9c4424d 100644
--- a/README.md
+++ b/README.md
@@ -18,3 +18,6 @@ This repository relies heavily on the use of submodules, and it pulls in most of
     ./s/build
 
 If you got this far and all went well, you have built a server (it is linked from the `game` subdirectory), and it will have passed some basic functionality tests.
+
+* [![Static Analysis](https://scan.coverity.com/projects/6742/badge.svg?flat=1)](https://scan.coverity.com/projects/6742/)
+* [![Build Status](https://api.travis-ci.org/eressea/server.svg?branch=develop)](https://travis-ci.org/eressea/server)

From 77a8c24ead10bb8cadf6c5d257c4629fc31d133b Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sat, 10 Feb 2018 18:52:03 +0100
Subject: [PATCH 14/40] disable process test turn check (recent change). make
 all tests run from inside the tests directory

---
 s/runtests                | 17 ++++++++---------
 scripts/tests/process.lua |  2 +-
 2 files changed, 9 insertions(+), 10 deletions(-)

diff --git a/s/runtests b/s/runtests
index 7a39670dd..b74a4b785 100755
--- a/s/runtests
+++ b/s/runtests
@@ -4,12 +4,12 @@ set -e
 ROOT=$(git rev-parse --show-toplevel)
 [ -z $BUILD ] && BUILD=Debug ; export BUILD
 
-UNIT_TESTS=$BUILD/eressea/test_eressea
-RUN_TESTS=$BUILD/eressea/eressea
+UNIT_TESTS=$ROOT/$BUILD/eressea/test_eressea
+RUN_TESTS=$ROOT/$BUILD/eressea/eressea
 if [ "$1" = "-V" ]; then
 VALGRIND=$(which valgrind)
 if [ -n "$VALGRIND" ]; then
-SUPP=share/ubuntu-12_04.supp
+SUPP=$ROOT/share/ubuntu-12_04.supp
 UNIT_TESTS="$VALGRIND --quiet --suppressions=$SUPP --error-exitcode=1 --leak-check=no $UNIT_TESTS"
 RUN_TESTS="$VALGRIND --quiet --suppressions=$SUPP --error-exitcode=1 --leak-check=no $RUN_TESTS"
 fi
@@ -21,12 +21,11 @@ if [ ! -e $ROOT/$BUILD ]; then
 fi
 
 $UNIT_TESTS
-cd $ROOT
-[ -e eressea.ini ] || ln -sf conf/eressea.ini
-$RUN_TESTS -v1 scripts/run-tests.lua
-$RUN_TESTS -v1 scripts/run-tests-e2.lua
-$RUN_TESTS -v1 scripts/run-tests-e3.lua
+cd $ROOT/tests
+$RUN_TESTS -v1 ../scripts/run-tests.lua
+$RUN_TESTS -v1 ../scripts/run-tests-e2.lua
+$RUN_TESTS -v1 ../scripts/run-tests-e3.lua
 $RUN_TESTS --version
-rm -rf data reports orders.txt score score.alliances datum turn
+rm -rf reports orders.txt score score.alliances datum turn
 
 cd $OLDWPD
diff --git a/scripts/tests/process.lua b/scripts/tests/process.lua
index f6ea63933..6288a8134 100644
--- a/scripts/tests/process.lua
+++ b/scripts/tests/process.lua
@@ -37,5 +37,5 @@ function test_process_turn()
     assert_file("reports/reports.txt")
     os.remove("reports")
     os.remove("data")
-    assert_equal(turn+1, get_turn())
+--    assert_equal(turn+1, get_turn())
 end

From c6ba91981c6ab07e3e8c13ffbc8ac69111a961c8 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sat, 10 Feb 2018 21:13:09 +0100
Subject: [PATCH 15/40] fix turn processing check

---
 scripts/tests/process.lua | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/scripts/tests/process.lua b/scripts/tests/process.lua
index 6288a8134..0006fcd74 100644
--- a/scripts/tests/process.lua
+++ b/scripts/tests/process.lua
@@ -2,11 +2,10 @@ require "lunit"
 
 module("tests.process", package.seeall, lunit.testcase)
 
-local u, r, f, turn
+local u, r, f
 
 function setup()
     eressea.free_game()
-    turn = get_turn()
     r = region.create(0, 0, "plain")
     f = faction.create("human", "bernd@eressea.de", "de")
     u = unit.create(f, r, 1)
@@ -25,8 +24,11 @@ local function assert_file(filename, exists)
 end
 
 function test_process_turn()
+    turn = get_turn()
+    turn_begin()
+    assert_equal(turn+1, get_turn())
     u:add_order("NUMMER PARTEI 777")
-    process_orders()
+    turn_process()
     assert_equal(0, init_reports())
     assert_equal(0, write_reports())
     assert_equal(0, eressea.write_game("test.dat"))
@@ -37,5 +39,6 @@ function test_process_turn()
     assert_file("reports/reports.txt")
     os.remove("reports")
     os.remove("data")
---    assert_equal(turn+1, get_turn())
+    turn_end()
+    assert_equal(turn+1, get_turn())
 end

From e06248cf1e290c375b45b61ccbbe7c4306cce488 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sat, 10 Feb 2018 21:24:38 +0100
Subject: [PATCH 16/40] remove residual data for an unfinished "city" feature.

---
 src/kernel/building.c | 19 +++++++++----------
 1 file changed, 9 insertions(+), 10 deletions(-)

diff --git a/src/kernel/building.c b/src/kernel/building.c
index 3bbd5891d..0e98cef7a 100644
--- a/src/kernel/building.c
+++ b/src/kernel/building.c
@@ -673,17 +673,16 @@ building *largestbuilding(const region * r, cmp_building_cb cmp_gt,
     }
     return best;
 }
-/* Lohn bei den einzelnen Burgstufen f�r Normale Typen, Orks, Bauern,
- * Modifikation f�r St�dter. */
+/* Lohn bei den einzelnen Burgstufen f�r Normale Typen, Orks, Bauern */
 
-static const int wagetable[7][4] = {
-    { 10, 10, 11, -7 },             /* Baustelle */
-    { 10, 10, 11, -5 },             /* Handelsposten */
-    { 11, 11, 12, -3 },             /* Befestigung */
-    { 12, 11, 13, -1 },             /* Turm */
-    { 13, 12, 14, 0 },              /* Burg */
-    { 14, 12, 15, 1 },              /* Festung */
-    { 15, 13, 16, 2 }               /* Zitadelle */
+static const int wagetable[7][3] = {
+    { 10, 10, 11 },             /* Baustelle */
+    { 10, 10, 11 },             /* Handelsposten */
+    { 11, 11, 12 },             /* Befestigung */
+    { 12, 11, 13 },             /* Turm */
+    { 13, 12, 14 },             /* Burg */
+    { 14, 12, 15 },             /* Festung */
+    { 15, 13, 16 }              /* Zitadelle */
 };
 
 static int

From ff33073bd9a53f73fb9bdcaee11e569c7ad77ab9 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sun, 11 Feb 2018 09:43:40 +0100
Subject: [PATCH 17/40] unused function

---
 src/modules/autoseed.c | 52 ------------------------------------------
 1 file changed, 52 deletions(-)

diff --git a/src/modules/autoseed.c b/src/modules/autoseed.c
index 17ea1084f..71aa6aae4 100644
--- a/src/modules/autoseed.c
+++ b/src/modules/autoseed.c
@@ -81,58 +81,6 @@ const terrain_type *random_terrain(const terrain_type * terrains[],
     return terrain;
 }
 
-static int count_demand(const region * r)
-{
-    struct demand *dmd;
-    int c = 0;
-    if (r->land) {
-        for (dmd = r->land->demands; dmd; dmd = dmd->next)
-            c++;
-    }
-    return c;
-}
-
-static int
-recurse_regions(region * r, region_list ** rlist,
-    bool(*fun) (const region * r))
-{
-    if (!fun(r))
-        return 0;
-    else {
-        int len = 0;
-        direction_t d;
-        region_list *rl = calloc(sizeof(region_list), 1);
-        rl->next = *rlist;
-        rl->data = r;
-        (*rlist) = rl;
-        fset(r, RF_MARK);
-        for (d = 0; d != MAXDIRECTIONS; ++d) {
-            region *nr = rconnect(r, d);
-            if (nr && !fval(nr, RF_MARK))
-                len += recurse_regions(nr, rlist, fun);
-        }
-        return len + 1;
-    }
-}
-
-static bool f_nolux(const region * r)
-{
-    return (r->land && count_demand(r) != get_maxluxuries());
-}
-
-int fix_all_demand(region *rd) {
-    region_list *rl, *rlist = NULL;
-    recurse_regions(rd, &rlist, f_nolux);
-    for (rl = rlist; rl; rl = rl->next) {
-        region *r = rl->data;
-        freset(r, RF_MARK);         /* undo recursive marker */
-        if (!fix_demand(r)) {
-            return -1;
-        }
-    }
-    return 0;
-}
-
 /* nach 150 Runden ist Neustart erlaubt */
 #define MINAGE_MULTI 150
 newfaction *read_newfactions(const char *filename)

From f67a4943e46bdcc3ed6a7bddd4ca744ce551955f Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sun, 11 Feb 2018 15:43:24 +0100
Subject: [PATCH 18/40] fix some cppcheck warnings

---
 src/kernel/build.c        |  5 ++---
 src/kernel/config.c       | 33 ---------------------------------
 src/kernel/connection.c   |  4 +---
 src/kernel/order.c        |  2 +-
 src/kernel/terrain.c      |  2 +-
 src/modules/autoseed.c    |  2 +-
 src/modules/museum.c      |  5 ++---
 src/races/zombies.c       |  4 +---
 src/triggers/changerace.c |  2 +-
 src/util/language.c       |  5 +++--
 src/util/unicode.c        |  3 ---
 11 files changed, 13 insertions(+), 54 deletions(-)

diff --git a/src/kernel/build.c b/src/kernel/build.c
index 677cf96fc..60ac94e53 100644
--- a/src/kernel/build.c
+++ b/src/kernel/build.c
@@ -472,16 +472,15 @@ static int count_materials(unit *u, const construction *type, int n, int complet
         int c;
         for (c = 0; n > 0 && type->materials[c].number; c++) {
             const struct resource_type *rtype = type->materials[c].rtype;
-            int need, prebuilt;
             int canuse = get_pooled(u, rtype, GET_DEFAULT, INT_MAX);
             canuse = matmod(u, rtype, canuse);
 
             assert(canuse >= 0);
             if (type->reqsize > 1) {
-                prebuilt =
+                int prebuilt =
                     required(completed, type->reqsize, type->materials[c].number);
                 for (; n;) {
-                    need =
+                    int need =
                         required(completed + n, type->reqsize, type->materials[c].number);
                     if (need - prebuilt <= canuse)
                         break;
diff --git a/src/kernel/config.c b/src/kernel/config.c
index 9a5ba0104..2895ed70a 100644
--- a/src/kernel/config.c
+++ b/src/kernel/config.c
@@ -147,39 +147,6 @@ const char *parameters[MAXPARAMS] = {
     "ALLIANZ"
 };
 
-FILE *debug;
-
-void
-parse(keyword_t kword, int(*dofun) (unit *, struct order *), bool thisorder)
-{
-    region *r;
-
-    for (r = regions; r; r = r->next) {
-        unit **up = &r->units;
-        while (*up) {
-            unit *u = *up;
-            order **ordp = &u->orders;
-            if (thisorder)
-                ordp = &u->thisorder;
-            while (*ordp) {
-                order *ord = *ordp;
-                if (getkeyword(ord) == kword) {
-                    if (dofun(u, ord) != 0)
-                        break;
-                    if (u->orders == NULL)
-                        break;
-                }
-                if (thisorder)
-                    break;
-                if (*ordp == ord)
-                    ordp = &ord->next;
-            }
-            if (*up == u)
-                up = &u->next;
-        }
-    }
-}
-
 int findoption(const char *s, const struct locale *lang)
 {
     void **tokens = get_translations(lang, UT_OPTIONS);
diff --git a/src/kernel/connection.c b/src/kernel/connection.c
index 516cb6ead..6f73c94f2 100644
--- a/src/kernel/connection.c
+++ b/src/kernel/connection.c
@@ -225,7 +225,7 @@ border_type *find_bordertype(const char *name)
 void b_read(connection * b, gamedata * data)
 {
     storage * store = data->store;
-    int n, result = 0;
+    int n;
     switch (b->type->datatype) {
     case VAR_NONE:
     case VAR_INT:
@@ -240,9 +240,7 @@ void b_read(connection * b, gamedata * data)
     case VAR_VOIDPTR:
     default:
         assert(!"invalid variant type in connection");
-        result = 0;
     }
-    assert(result >= 0 || "EOF encountered?");
 }
 
 void b_write(const connection * b, storage * store)
diff --git a/src/kernel/order.c b/src/kernel/order.c
index 8defd05c9..807541ac1 100644
--- a/src/kernel/order.c
+++ b/src/kernel/order.c
@@ -242,10 +242,10 @@ order *create_order(keyword_t kwd, const struct locale * lang,
         va_start(marker, params);
         sbs_init(&sbs, zBuffer, sizeof(zBuffer));
         while (*params) {
-            int i;
             const char *s;
             tok = strchr(params, '%');
             if (tok) {
+                int i;
                 if (tok != params) {
                     sbs_strncat(&sbs, params, tok - params);
                 }
diff --git a/src/kernel/terrain.c b/src/kernel/terrain.c
index 5a46b7fe7..6a5b4652d 100644
--- a/src/kernel/terrain.c
+++ b/src/kernel/terrain.c
@@ -71,12 +71,12 @@ bool terrain_changed(int *cache) {
 void free_terrains(void)
 {
     while (registered_terrains) {
-        int n;
         terrain_type * t = registered_terrains;
         registered_terrains = t->next;
         free(t->_name);
         free(t->herbs);
         if (t->production) {
+            int n;
             for (n = 0; t->production[n].type; ++n) {
                 free(t->production[n].base);
                 free(t->production[n].divisor);
diff --git a/src/modules/autoseed.c b/src/modules/autoseed.c
index 71aa6aae4..374b6379c 100644
--- a/src/modules/autoseed.c
+++ b/src/modules/autoseed.c
@@ -807,9 +807,9 @@ static void smooth_island(region_list * island)
     region_list *rlist = NULL;
     for (rlist = island; rlist; rlist = rlist->next) {
         region *r = rlist->data;
-        int n, nland = 0;
 
         if (r->land) {
+            int n, nland = 0;
             get_neighbours(r, rn);
             for (n = 0; n != MAXDIRECTIONS && nland <= 1; ++n) {
                 if (rn[n]->land) {
diff --git a/src/modules/museum.c b/src/modules/museum.c
index d854d1763..9daa623ef 100644
--- a/src/modules/museum.c
+++ b/src/modules/museum.c
@@ -301,7 +301,6 @@ order * ord)
     attrib *a;
     region *r;
     unit *warden = findunit(atoi36("mwar"));
-    int unit_cookie;
 
     UNUSED_ARG(amount);
 
@@ -316,11 +315,11 @@ order * ord)
     r = findregion(a->data.sa[0], a->data.sa[1]);
     assert(r);
     a_remove(&u->attribs, a);
-    /* �bergebene Gegenst�nde zur�ckgeben */
+    /* Übergebene Gegenstände zurückgeben */
 
     a = a_find(u->attribs, &at_museumgivebackcookie);
     if (a) {
-        unit_cookie = a->data.i;
+        int unit_cookie = a->data.i;
         a_remove(&u->attribs, a);
 
         for (a = a_find(warden->attribs, &at_museumgiveback);
diff --git a/src/races/zombies.c b/src/races/zombies.c
index 74bf53a76..de3d81972 100644
--- a/src/races/zombies.c
+++ b/src/races/zombies.c
@@ -43,7 +43,6 @@ void make_undead_unit(unit * u)
 void age_undead(unit * u)
 {
     region *r = u->region;
-    int n = 0;
 
     /* untote, die einer partei angehoeren, koennen sich
      * absplitten, anstatt sich zu vermehren. monster
@@ -51,10 +50,9 @@ void age_undead(unit * u)
 
     if (u->number > UNDEAD_MIN && !is_monsters(u->faction)
         && rng_int() % 100 < UNDEAD_BREAKUP) {
-        int m;
+        int m, n = 0;
         unit *u2;
 
-        n = 0;
         for (m = u->number; m; m--) {
             if (rng_int() % 100 < UNDEAD_BREAKUP_FRACTION)
                 ++n;
diff --git a/src/triggers/changerace.c b/src/triggers/changerace.c
index 5ab56b346..0f644c47b 100644
--- a/src/triggers/changerace.c
+++ b/src/triggers/changerace.c
@@ -111,7 +111,7 @@ trigger *trigger_changerace(struct unit * u, const struct race * prace,
     trigger *t = t_new(&tt_changerace);
     changerace_data *td = (changerace_data *)t->data.v;
 
-    assert(u_race(u) == u_irace(u) || "!changerace-triggers cannot stack!");
+    assert(u_race(u) == u_irace(u) || !"changerace-triggers cannot stack!");
     td->u = u;
     td->race = prace;
     td->irace = irace;
diff --git a/src/util/language.c b/src/util/language.c
index 689ccfcd7..cf76dc4de 100644
--- a/src/util/language.c
+++ b/src/util/language.c
@@ -257,8 +257,9 @@ static lstr lstrs[MAXLOCALES];
 void ** get_translations(const struct locale *lang, int index)
 {
     assert(lang);
-    assert(lang->index < MAXLOCALES
-        || "you have to increase MAXLOCALES and recompile");
+    if (lang->index >= MAXLOCALES) {
+        log_fatal("you have to increase MAXLOCALES and recompile");
+    }
     if (lang->index < MAXLOCALES) {
         return lstrs[lang->index].tokens + index;
     }
diff --git a/src/util/unicode.c b/src/util/unicode.c
index 10d43f947..85a341ddf 100644
--- a/src/util/unicode.c
+++ b/src/util/unicode.c
@@ -108,9 +108,6 @@ int unicode_utf8_tolower(utf8_t * op, size_t outlen, const utf8_t * ip)
         }
     }
 
-    if (outlen <= 0) {
-        return ENOMEM;
-    }
     *op = 0;
     return 0;
 }

From 398a25865827914eea70c4ae5189756d12301da0 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sun, 11 Feb 2018 15:57:31 +0100
Subject: [PATCH 19/40] cppcheck: variable scopes, etc.

---
 src/economy.c  | 14 +++++++-------
 src/give.c     | 10 +++-------
 src/gmtool.c   | 13 +++++++------
 src/jsonconf.c |  3 ++-
 src/laws.c     |  2 +-
 src/magic.c    |  4 ++--
 src/move.c     |  5 +++--
 src/names.c    | 21 ++++++++++-----------
 src/piracy.c   |  2 +-
 src/prefix.c   |  2 +-
 src/spells.c   |  4 ++--
 11 files changed, 39 insertions(+), 41 deletions(-)

diff --git a/src/economy.c b/src/economy.c
index a992729f4..995705aa6 100644
--- a/src/economy.c
+++ b/src/economy.c
@@ -333,7 +333,7 @@ static int do_recruiting(recruitment * recruits, int available)
         for (req = rec->requests; req; req = req->next) {
             unit *u = req->unit;
             const race *rc = u_race(u); /* race is set in recruit() */
-            int number, dec;
+            int number;
             double multi = 2.0 * rc->recruit_multi;
 
             number = (int)(get / multi);
@@ -359,7 +359,7 @@ static int do_recruiting(recruitment * recruits, int available)
             }
             add_recruits(u, number, req->qty);
             if (number > 0) {
-                dec = (int)(number * multi);
+                int dec = (int)(number * multi);
                 if ((rc->ec_flags & ECF_REC_ETHEREAL) == 0) {
                     recruited += dec;
                 }
@@ -1006,7 +1006,7 @@ static void allocate_resource(unit * u, const resource_type * rtype, int want)
         }
     }
 
-    assert(sk != NOSKILL || "limited resource needs a required skill for making it");
+    assert(sk != NOSKILL || !"limited resource needs a required skill for making it");
     skill = effskill(u, sk, 0);
     if (skill == 0) {
         add_message(&u->faction->msgs,
@@ -1075,13 +1075,12 @@ leveled_allocation(const resource_type * rtype, region * r, allocation * alist)
 {
     const item_type *itype = resource2item(rtype);
     rawmaterial *rm = rm_get(r, rtype);
-    int need;
     bool first = true;
 
     if (rm != NULL) {
+        int need;
         do {
-            int avail = rm->amount;
-            int nreq = 0;
+            int avail = rm->amount, nreq = 0;
             allocation *al;
 
             if (avail <= 0) {
@@ -1093,7 +1092,7 @@ leveled_allocation(const resource_type * rtype, region * r, allocation * alist)
 
             assert(avail > 0);
 
-            for (al = alist; al; al = al->next)
+            for (al = alist; al; al = al->next) {
                 if (!fval(al, AFL_DONE)) {
                     int req = required(al->want - al->get, al->save);
                     assert(al->get <= al->want && al->get >= 0);
@@ -1112,6 +1111,7 @@ leveled_allocation(const resource_type * rtype, region * r, allocation * alist)
                             fset(al, AFL_LOWSKILL);
                     }
                 }
+            }
             need = nreq;
 
             if (avail > nreq) avail = nreq;
diff --git a/src/give.c b/src/give.c
index 5d5673bd2..21e099394 100644
--- a/src/give.c
+++ b/src/give.c
@@ -158,18 +158,15 @@ static bool limited_give(const item_type * type)
 int give_quota(const unit * src, const unit * dst, const item_type * type,
     int n)
 {
-    double divisor;
-
     if (!limited_give(type)) {
         return n;
     }
     if (dst && src && src->faction != dst->faction) {
-        divisor = config_get_flt("rules.items.give_divisor", 1);
+        int divisor = config_get_int("rules.items.give_divisor", 1);
         assert(divisor <= 0 || divisor >= 1);
         if (divisor >= 1) {
             /* predictable > correct: */
-            int x = (int)(n / divisor);
-            return x;
+            return n / divisor;
         }
     }
     return n;
@@ -306,7 +303,6 @@ static bool rule_transfermen(void)
 message * give_men(int n, unit * u, unit * u2, struct order *ord)
 {
     ship *sh;
-    int k = 0;
     int error = 0;
     message * msg;
     int maxt = max_transfers();
@@ -391,7 +387,7 @@ message * give_men(int n, unit * u, unit * u2, struct order *ord)
     }
 
     if (has_skill(u, SK_ALCHEMY) || has_skill(u2, SK_ALCHEMY)) {
-        k = count_skill(u2->faction, SK_ALCHEMY);
+        int k = count_skill(u2->faction, SK_ALCHEMY);
 
         /* Falls die Zieleinheit keine Alchemisten sind, werden sie nun
          * welche. */
diff --git a/src/gmtool.c b/src/gmtool.c
index d74f1e46b..51a86e16c 100644
--- a/src/gmtool.c
+++ b/src/gmtool.c
@@ -137,10 +137,10 @@ int umvwaddnstr(WINDOW *w, int y, int x, const char * str, int len) {
 
 static void init_curses(void)
 {
-    int fg, bg;
     initscr();
 
     if (has_colors() || force_color) {
+        int fg, bg;
         short bcol = COLOR_BLACK;
         short hcol = COLOR_MAGENTA;
         start_color();
@@ -316,11 +316,11 @@ static void paint_map(window * wnd, const state * st)
         int yp = (lines - vy - 1) * THEIGHT;
         for (vx = 0; vx != cols; ++vx) {
             map_region *mr = mr_get(&st->display, vx, vy);
-            int attr = 0;
-            int hl = 0;
             int xp = vx * TWIDTH + (vy & 1) * TWIDTH / 2;
             int nx, ny;
             if (mr) {
+                int attr = 0;
+                int hl = 0;
                 cnormalize(&mr->coord, &nx, &ny);
                 if (tagged_region(st->selected, nx, ny)) {
                     attr |= A_REVERSE;
@@ -336,9 +336,9 @@ static void paint_map(window * wnd, const state * st)
 map_region *cursor_region(const view * v, const coordinate * c)
 {
     coordinate relpos;
-    int cx, cy;
 
     if (c) {
+        int cx, cy;
         relpos.x = c->x - v->topleft.x;
         relpos.y = c->y - v->topleft.y;
         cy = relpos.y;
@@ -426,13 +426,14 @@ static void paint_info_region(window * wnd, const state * st)
 {
     WINDOW *win = wnd->handle;
     int size = getmaxx(win) - 2;
-    int line = 0, maxline = getmaxy(win) - 2;
+    int maxline = getmaxy(win) - 2;
     map_region *mr = cursor_region(&st->display, &st->cursor);
 
     UNUSED_ARG(st);
     werase(win);
     wxborder(win);
     if (mr && mr->r) {
+        int line = 0;
         const region *r = mr->r;
         if (r->land) {
             umvwaddnstr(win, line++, 1, (char *)r->land->name, size);
@@ -708,10 +709,10 @@ static void select_regions(state * st, int selectmode)
     doupdate();
     findmode = getch();
     if (findmode == 'n') {        /* none */
-        int i;
         sprintf(sbuffer, "%snone", status);
         statusline(st->wnd_status->handle, sbuffer);
         if (selectmode & MODE_SELECT) {
+            int i;
             for (i = 0; i != MAXTHASH; ++i) {
                 tag **tp = &st->selected->tags[i];
                 while (*tp) {
diff --git a/src/jsonconf.c b/src/jsonconf.c
index dcab0e9c6..ed10f558f 100644
--- a/src/jsonconf.c
+++ b/src/jsonconf.c
@@ -124,7 +124,7 @@ static void json_maintenance_i(cJSON *json, maintenance *mt) {
 static void json_maintenance(cJSON *json, maintenance **mtp) {
     cJSON *child;
     maintenance *mt;
-    int i, size = 1;
+    int size = 1;
 
     if (json->type == cJSON_Array) {
         size = cJSON_GetArraySize(json);
@@ -135,6 +135,7 @@ static void json_maintenance(cJSON *json, maintenance **mtp) {
     }
     *mtp = mt = (struct maintenance *) calloc(sizeof(struct maintenance), size + 1);
     if (json->type == cJSON_Array) {
+        int i;
         for (i = 0, child = json->child; child; child = child->next, ++i) {
             if (child->type == cJSON_Object) {
                 json_maintenance_i(child, mt + i);
diff --git a/src/laws.c b/src/laws.c
index ac8b3d612..a31d67589 100644
--- a/src/laws.c
+++ b/src/laws.c
@@ -2720,10 +2720,10 @@ static void age_stonecircle(building *b) {
             curse *c = get_curse(rt->attribs, &ct_astralblock);
             if (!c) {
                 int sk = effskill(mage, SK_MAGIC, 0);
-                float effect = 100;
                 if (sk > 0) {
                     int vig = sk;
                     int dur = (sk + 1) / 2;
+                    float effect = 100;
                     /* the mage reactivates the circle */
                     c = create_curse(mage, &rt->attribs, &ct_astralblock,
                         vig, dur, effect, 0);
diff --git a/src/magic.c b/src/magic.c
index 5cfd4d071..b64ec221a 100644
--- a/src/magic.c
+++ b/src/magic.c
@@ -499,13 +499,13 @@ void unset_combatspell(unit * u, spell * sp)
 {
     sc_mage *m;
     int nr = 0;
-    int i;
 
     m = get_mage_depr(u);
     if (!m)
         return;
 
     if (!sp) {
+        int i;
         for (i = 0; i < MAXCOMBATSPELLS; i++) {
             m->combatspells[i].sp = NULL;
         }
@@ -1589,13 +1589,13 @@ verify_targets(castorder * co, int *invalid, int *resist, int *success)
     const spell *sp = co->sp;
     region *target_r = co_get_region(co);
     spellparameter *sa = co->par;
-    int i;
 
     *invalid = 0;
     *resist = 0;
     *success = 0;
 
     if (sa && sa->length) {
+        int i;
         /* zuerst versuchen wir vorher nicht gefundene Objekte zu finden.
          * Wurde ein Objekt durch globalsuche gefunden, obwohl der Zauber
          * gar nicht global hätte suchen dürften, setzen wir das Objekt
diff --git a/src/move.c b/src/move.c
index 3a007d26d..72d311395 100644
--- a/src/move.c
+++ b/src/move.c
@@ -284,7 +284,7 @@ static int ridingcapacity(const unit * u)
 int walkingcapacity(const struct unit *u)
 {
     int n, people, pferde_fuer_wagen, horses;
-    int wagen_ohne_pferde, wagen_mit_pferden, wagen_mit_trollen;
+    int wagen_mit_pferden;
     int vehicles = 0, vcap = 0;
     int animals = 0, acap = 0;
     const struct resource_type *rhorse = rt_find("horse");
@@ -312,6 +312,7 @@ int walkingcapacity(const struct unit *u)
     n = wagen_mit_pferden * vcap;
 
     if (u_race(u) == get_race(RC_TROLL)) {
+        int wagen_ohne_pferde, wagen_mit_trollen;
         /* 4 Trolle ziehen einen Wagen. */
         /* Unbesetzte Wagen feststellen */
         wagen_ohne_pferde = vehicles - wagen_mit_pferden;
@@ -682,7 +683,6 @@ static bool is_freezing(const unit * u)
 
 int check_ship_allowed(struct ship *sh, const region * r)
 {
-    int c = 0;
     const building_type *bt_harbour = bt_find("harbour");
 
     if (sh->region && r_insectstalled(r)) {
@@ -708,6 +708,7 @@ int check_ship_allowed(struct ship *sh, const region * r)
         return SA_COAST;
     }
     if (sh->type->coasts) {
+        int c;
         for (c = 0; sh->type->coasts[c] != NULL; ++c) {
             if (sh->type->coasts[c] == r->terrain) {
                 return SA_COAST;
diff --git a/src/names.c b/src/names.c
index d8abc077b..1bb6014a6 100644
--- a/src/names.c
+++ b/src/names.c
@@ -350,7 +350,6 @@ static void dracoid_name(unit * u)
 {
     static char name[NAMESIZE + 1];
     int mid_syllabels;
-    size_t sz;
 
     /* ignore u */
     UNUSED_ARG(u);
@@ -358,14 +357,14 @@ static void dracoid_name(unit * u)
 
     mid_syllabels = rng_int() % 4;
 
-    sz = str_strlcpy(name, drac_pre[rng_int() % DRAC_PRE], sizeof(name));
+    str_strlcpy(name, drac_pre[rng_int() % DRAC_PRE], sizeof(name));
     while (mid_syllabels > 0) {
         mid_syllabels--;
         if (rng_int() % 10 < 4)
             str_strlcat(name, "'", sizeof(name));
-        sz += str_strlcat(name, drac_mid[rng_int() % DRAC_MID], sizeof(name));
+        str_strlcat(name, drac_mid[rng_int() % DRAC_MID], sizeof(name));
     }
-    sz += str_strlcat(name, drac_suf[rng_int() % DRAC_SUF], sizeof(name));
+    str_strlcat(name, drac_suf[rng_int() % DRAC_SUF], sizeof(name));
     unit_setname(u, name);
 }
 
@@ -392,13 +391,13 @@ const char *abkz(const char *s, char *buf, size_t buflen, size_t maxchars)
     while (*p != 0) {
 
         result = unicode_utf8_to_ucs4(&ucs, p, &size);
-        assert(result == 0 || "damnit, we're not handling invalid input here!");
+        assert(result == 0 || !"damnit, we're not handling invalid input here!");
 
         /* Leerzeichen �berspringen */
         while (*p != 0 && !iswalnum((wint_t)ucs)) {
             p += size;
             result = unicode_utf8_to_ucs4(&ucs, p, &size);
-            assert(result == 0 || "damnit, we're not handling invalid input here!");
+            assert(result == 0 || !"damnit, we're not handling invalid input here!");
         }
 
         /* Counter erh�hen */
@@ -409,7 +408,7 @@ const char *abkz(const char *s, char *buf, size_t buflen, size_t maxchars)
         while (*p != 0 && iswalnum((wint_t)ucs)) {
             p += size;
             result = unicode_utf8_to_ucs4(&ucs, p, &size);
-            assert(result == 0 || "damnit, we're not handling invalid input here!");
+            assert(result == 0 || !"damnit, we're not handling invalid input here!");
         }
     }
 
@@ -423,7 +422,7 @@ const char *abkz(const char *s, char *buf, size_t buflen, size_t maxchars)
     bufp = buf;
 
     result = unicode_utf8_to_ucs4(&ucs, p, &size);
-    assert(result == 0 || "damnit, we're not handling invalid input here!");
+    assert(result == 0 || !"damnit, we're not handling invalid input here!");
 
     while (*p != 0 && c < maxchars) {
         /* Leerzeichen �berspringen */
@@ -431,7 +430,7 @@ const char *abkz(const char *s, char *buf, size_t buflen, size_t maxchars)
         while (*p != 0 && !iswalnum((wint_t)ucs)) {
             p += size;
             result = unicode_utf8_to_ucs4(&ucs, p, &size);
-            assert(result == 0 || "damnit, we're not handling invalid input here!");
+            assert(result == 0 || !"damnit, we're not handling invalid input here!");
         }
 
         /* alnums �bertragen */
@@ -443,7 +442,7 @@ const char *abkz(const char *s, char *buf, size_t buflen, size_t maxchars)
             ++c;
 
             result = unicode_utf8_to_ucs4(&ucs, p, &size);
-            assert(result == 0 || "damnit, we're not handling invalid input here!");
+            assert(result == 0 || !"damnit, we're not handling invalid input here!");
         }
 
         /* Bis zum n�chsten Leerzeichen */
@@ -451,7 +450,7 @@ const char *abkz(const char *s, char *buf, size_t buflen, size_t maxchars)
         while (c < maxchars && *p != 0 && iswalnum((wint_t)ucs)) {
             p += size;
             result = unicode_utf8_to_ucs4(&ucs, p, &size);
-            assert(result == 0 || "damnit, we're not handling invalid input here!");
+            assert(result == 0 || !"damnit, we're not handling invalid input here!");
         }
     }
 
diff --git a/src/piracy.c b/src/piracy.c
index da0e4a3e1..46977c3d8 100644
--- a/src/piracy.c
+++ b/src/piracy.c
@@ -122,7 +122,6 @@ void piracy_cmd(unit * u)
         const faction *target;
         int value;
     } aff[MAXDIRECTIONS];
-    int saff = 0;
     int *il;
 
     assert(u->thisorder);
@@ -139,6 +138,7 @@ void piracy_cmd(unit * u)
     /* Wenn nicht, sehen wir, ob wir ein Ziel finden. */
 
     if (target_dir == NODIRECTION) {
+        int saff = 0;
         direction_t dir;
         /* Einheit ist also Kapit�n. Jetzt gucken, in wievielen
         * Nachbarregionen potentielle Opfer sind. */
diff --git a/src/prefix.c b/src/prefix.c
index f40a08091..84c026625 100644
--- a/src/prefix.c
+++ b/src/prefix.c
@@ -37,8 +37,8 @@ int add_raceprefix(const char *prefix)
 }
 
 void free_prefixes(void) {
-    int i;
     if (race_prefixes) {
+        int i;
         for (i = 0; race_prefixes[i]; ++i) {
             free(race_prefixes[i]);
         }
diff --git a/src/spells.c b/src/spells.c
index 12778e9d4..cc7eed8b1 100644
--- a/src/spells.c
+++ b/src/spells.c
@@ -537,7 +537,6 @@ static int sp_summon_familiar(castorder * co)
     unit *mage = co->magician.u;
     int cast_level = co->level;
     const race *rc;
-    int dh;
     message *msg;
     char zText[2048];
 
@@ -553,7 +552,8 @@ static int sp_summon_familiar(castorder * co)
 
     if (fval(rc, RCF_SWIM) && !fval(rc, RCF_WALK)) {
         int coasts = is_coastregion(r);
-        int dir;
+        int dir, dh;
+
         if (coasts == 0) {
             cmistake(mage, co->order, 229, MSG_MAGIC);
             return 0;

From b5ea102372316a43882467875f24bcd2f2c4507f Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sun, 11 Feb 2018 16:14:00 +0100
Subject: [PATCH 20/40] cppcheck: scope reductions and a false positive.

---
 src/economy.c | 15 ++++++---------
 src/report.c  |  2 +-
 src/reports.c |  8 ++++----
 src/study.c   |  8 +++++---
 4 files changed, 16 insertions(+), 17 deletions(-)

diff --git a/src/economy.c b/src/economy.c
index 995705aa6..219d818f9 100644
--- a/src/economy.c
+++ b/src/economy.c
@@ -1190,15 +1190,13 @@ attrib_allocation(const resource_type * rtype, region * r, allocation * alist)
     assert(avail == 0 || nreq == 0);
 }
 
-typedef void(*allocate_function) (const resource_type *, struct region *,
-    struct allocation *);
-
-static allocate_function get_allocator(const struct resource_type *rtype)
-{
+static void allocate(const resource_type *rtype, region *r, allocation *data) {
     if (rtype->raw) {
-        return leveled_allocation;
+        leveled_allocation(rtype, r, data);
+    }
+    else {
+        attrib_allocation(rtype, r, data);
     }
-    return attrib_allocation;
 }
 
 void split_allocations(region * r)
@@ -1207,11 +1205,10 @@ void split_allocations(region * r)
     while (*p_alist) {
         allocation_list *alist = *p_alist;
         const resource_type *rtype = alist->type;
-        allocate_function alloc = get_allocator(rtype);
         const item_type *itype = resource2item(rtype);
         allocation **p_al = &alist->data;
 
-        alloc(rtype, r, alist->data);
+        allocate(rtype, r, alist->data);
 
         while (*p_al) {
             allocation *al = *p_al;
diff --git a/src/report.c b/src/report.c
index 30ea34fdf..9e98e5162 100644
--- a/src/report.c
+++ b/src/report.c
@@ -304,7 +304,6 @@ void nr_spell_syntax(struct stream *out, spellbook_entry * sbe, const struct loc
         };
         starget *targetp;
         char cp = *params++;
-        int i, maxparam = 0;
         const char *locp;
         const char *syntaxp = sp->syntax;
 
@@ -359,6 +358,7 @@ void nr_spell_syntax(struct stream *out, spellbook_entry * sbe, const struct loc
                 WARN_STATIC_BUFFER();
         }
         else if (cp == 'k') {
+            int i, maxparam = 0;
             bool multi = false;
             if (params && *params == 'c') {
                 /* skip over a potential id */
diff --git a/src/reports.c b/src/reports.c
index 602edb83c..d4be5c8ce 100644
--- a/src/reports.c
+++ b/src/reports.c
@@ -575,7 +575,6 @@ report_resources(const region * r, resource_report * result, int size,
     if (see_unit) {
         rawmaterial *res = r->resources;
         while (res) {
-            int maxskill = 0;
             const item_type *itype = resource2item(res->rtype);
             int minskill = itype->construction->minskill;
             skill_t skill = itype->construction->skill;
@@ -588,6 +587,7 @@ report_resources(const region * r, resource_report * result, int size,
             }
             else {
                 const unit *u;
+                int maxskill = 0;
                 for (u = r->units; visible != res->amount && u != NULL; u = u->next) {
                     if (u->faction == viewer) {
                         int s = effskill(u, skill, 0);
@@ -923,7 +923,7 @@ spskill(char *buffer, size_t size, const struct locale * lang,
     const struct unit * u, struct skill * sv, int *dh, int days)
 {
     char *bufp = buffer;
-    int i, effsk;
+    int effsk;
 
     if (!u->number)
         return 0;
@@ -953,7 +953,7 @@ spskill(char *buffer, size_t size, const struct locale * lang,
     }
 
     if (sv->id == SK_STEALTH && fval(u, UFL_STEALTH)) {
-        i = u_geteffstealth(u);
+        int i = u_geteffstealth(u);
         if (i >= 0) {
             if (wrptr(&bufp, &size, snprintf(bufp, size, "%d/", i)) != 0)
                 WARN_STATIC_BUFFER();
@@ -2276,7 +2276,6 @@ static void eval_trail(struct opstack **stack, const void *userdata)
 {                               /* order -> string */
     const faction *report = (const faction *)userdata;
     const struct locale *lang = report ? report->locale : default_locale;
-    int i, end = 0, begin = 0;
     const arg_regions *aregs = (const arg_regions *)opop(stack).v;
     char buf[512];
     size_t size = sizeof(buf) - 1;
@@ -2288,6 +2287,7 @@ static void eval_trail(struct opstack **stack, const void *userdata)
 #endif
 
     if (aregs != NULL) {
+        int i, end = 0, begin = 0;
         end = aregs->nregions;
         for (i = begin; i < end; ++i) {
             region *r = aregs->regions[i];
diff --git a/src/study.c b/src/study.c
index d9e66f1f9..1dbc890ed 100644
--- a/src/study.c
+++ b/src/study.c
@@ -475,10 +475,10 @@ typedef enum study_rule_t {
 static double study_speedup(unit * u, skill_t s, study_rule_t rule)
 {
 #define MINTURN 16
-    double learnweeks = 0;
-    int i;
     if (turn > MINTURN) {
         if (rule == STUDY_FASTER) {
+            double learnweeks = 0;
+            int i;
             for (i = 0; i != u->skill_size; ++i) {
                 skill *sv = u->skills + i;
                 if (sv->id == s) {
@@ -491,6 +491,8 @@ static double study_speedup(unit * u, skill_t s, study_rule_t rule)
             return 2.0; /* If the skill was not found it is the first study. */
         }
         if (rule == STUDY_AUTOTEACH) {
+            double learnweeks = 0;
+            int i;
             for (i = 0; i != u->skill_size; ++i) {
                 skill *sv = u->skills + i;
                 learnweeks += (sv->level * (sv->level + 1) / 2.0);
@@ -547,7 +549,6 @@ int study_cmd(unit * u, order * ord)
 {
     region *r = u->region;
     int p;
-    magic_t mtyp;
     int l;
     int studycost, days;
     double multi = 1.0;
@@ -621,6 +622,7 @@ int study_cmd(unit * u, order * ord)
     }
 
     if (sk == SK_MAGIC) {
+        magic_t mtyp;
         if (u->number > 1) {
             cmistake(u, ord, 106, MSG_MAGIC);
             return -1;

From f4be3a1374e5128a766381c7cabb63cac1a0a031 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Wed, 14 Feb 2018 11:24:38 +0100
Subject: [PATCH 21/40] remove duplication setstatus -> unit_setstatus

---
 src/kernel/save.c         |  2 +-
 src/kernel/unit.c         | 10 +---------
 src/kernel/unit.h         |  1 -
 src/laws.c                | 16 ++++++++--------
 src/monsters.c            |  4 ++--
 src/spells.c              |  6 +++---
 src/spells/combatspells.c |  8 ++++----
 7 files changed, 19 insertions(+), 28 deletions(-)

diff --git a/src/kernel/save.c b/src/kernel/save.c
index a4bfdcbca..a88ae6fb8 100644
--- a/src/kernel/save.c
+++ b/src/kernel/save.c
@@ -495,7 +495,7 @@ unit *read_unit(gamedata *data)
     }
 
     READ_INT(data->store, &n);
-    setstatus(u, n);
+    unit_setstatus(u, n);
     READ_INT(data->store, &u->flags);
     u->flags &= UFL_SAVEMASK;
     if ((u->flags & UFL_ANON_FACTION) && !rule_stealth_anon()) {
diff --git a/src/kernel/unit.c b/src/kernel/unit.c
index acc28fff3..3519b43b0 100644
--- a/src/kernel/unit.c
+++ b/src/kernel/unit.c
@@ -1530,7 +1530,7 @@ unit *create_unit(region * r, faction * f, int number, const struct race *urace,
         attrib *a;
 
         /* erbt Kampfstatus */
-        setstatus(u, creator->status);
+        unit_setstatus(u, creator->status);
 
         /* erbt Gebaeude/Schiff */
         if (creator->region == r) {
@@ -1985,14 +1985,6 @@ bool has_horses(const unit * u)
     return false;
 }
 
-void setstatus(unit *u, int status)
-{
-    assert(status >= ST_AGGRO && status <= ST_FLEE);
-    if (u->status != status) {
-        u->status = (status_t)status;
-    }
-}
-
 #define MAINTENANCE 10
 int maintenance_cost(const struct unit *u)
 {
diff --git a/src/kernel/unit.h b/src/kernel/unit.h
index 91a3ec930..e7bf6a9e4 100644
--- a/src/kernel/unit.h
+++ b/src/kernel/unit.h
@@ -267,7 +267,6 @@ extern "C" {
     int getunit(const struct region * r, const struct faction * f, struct unit **uresult);
     int read_unitid(const struct faction *f, const struct region *r);
 
-    void setstatus(struct unit *u, int status);
     /* !< sets combatstatus of a unit */
     int besieged(const struct unit *u);
     bool has_horses(const struct unit *u);
diff --git a/src/laws.c b/src/laws.c
index a31d67589..d09236875 100644
--- a/src/laws.c
+++ b/src/laws.c
@@ -2438,22 +2438,22 @@ int status_cmd(unit * u, struct order *ord)
     s = gettoken(token, sizeof(token));
     switch (findparam(s, u->faction->locale)) {
     case P_NOT:
-        setstatus(u, ST_AVOID);
+        unit_setstatus(u, ST_AVOID);
         break;
     case P_BEHIND:
-        setstatus(u, ST_BEHIND);
+        unit_setstatus(u, ST_BEHIND);
         break;
     case P_FLEE:
-        setstatus(u, ST_FLEE);
+        unit_setstatus(u, ST_FLEE);
         break;
     case P_CHICKEN:
-        setstatus(u, ST_CHICKEN);
+        unit_setstatus(u, ST_CHICKEN);
         break;
     case P_AGGRO:
-        setstatus(u, ST_AGGRO);
+        unit_setstatus(u, ST_AGGRO);
         break;
     case P_VORNE:
-        setstatus(u, ST_FIGHT);
+        unit_setstatus(u, ST_FIGHT);
         break;
     case P_HELP:
         if (getparam(u->faction->locale) == P_NOT) {
@@ -2469,7 +2469,7 @@ int status_cmd(unit * u, struct order *ord)
                 msg_feedback(u, ord, "unknown_status", ""));
         }
         else {
-            setstatus(u, ST_FIGHT);
+            unit_setstatus(u, ST_FIGHT);
         }
     }
     return 0;
@@ -2940,7 +2940,7 @@ void maketemp_cmd(unit *u, order **olist)
         if (sh) {
             set_leftship(u2, sh);
         }
-        setstatus(u2, u->status);
+        unit_setstatus(u2, u->status);
 
         /* copy orders until K_END from u to u2 */
         ordp = &makeord->next;
diff --git a/src/monsters.c b/src/monsters.c
index d1979414d..b68c5d576 100644
--- a/src/monsters.c
+++ b/src/monsters.c
@@ -615,11 +615,11 @@ static void recruit_dracoids(unit * dragon, int size)
     change_money(dragon, -un->number * 50);
     equip_unit(un, get_equipment("new_dracoid"));
 
-    setstatus(un, ST_FIGHT);
+    unit_setstatus(un, ST_FIGHT);
     for (weapon = un->items; weapon; weapon = weapon->next) {
         const weapon_type *wtype = weapon->type->rtype->wtype;
         if (wtype && wtype->flags & WTF_MISSILE) {
-            setstatus(un, ST_BEHIND);
+            unit_setstatus(un, ST_BEHIND);
             break;
         }
     }
diff --git a/src/spells.c b/src/spells.c
index cc7eed8b1..8612c92ef 100644
--- a/src/spells.c
+++ b/src/spells.c
@@ -518,7 +518,7 @@ static unit * make_familiar(unit * mage, region *r, const race *rc, const char *
     unit *fam;
 
     fam = create_unit(r, mage->faction, 1, rc, 0, name, mage);
-    setstatus(fam, ST_FLEE);
+    unit_setstatus(fam, ST_FLEE);
     fset(fam, UFL_LOCKED);
 
     /* triggers: */
@@ -2161,7 +2161,7 @@ static int sp_ironkeeper(castorder * co)
         create_unit(r, mage->faction, 1, get_race(RC_IRONKEEPER), 0, NULL, mage);
 
     /*keeper->age = cast_level + 2; */
-    setstatus(keeper, ST_AVOID);  /* kaempft nicht */
+    unit_setstatus(keeper, ST_AVOID);  /* kaempft nicht */
     setguard(keeper, true);
     fset(keeper, UFL_ISNEW);
     /* Parteitarnen, damit man nicht sofort wei�, wer dahinter steckt */
@@ -4630,7 +4630,7 @@ int sp_clonecopy(castorder * co)
     clone =
         create_unit(target_region, mage->faction, 1, get_race(RC_CLONE), 0, name,
             mage);
-    setstatus(clone, ST_FLEE);
+    unit_setstatus(clone, ST_FLEE);
     fset(clone, UFL_LOCKED);
 
     create_newclone(mage, clone);
diff --git a/src/spells/combatspells.c b/src/spells/combatspells.c
index adaa832d9..4f7974018 100644
--- a/src/spells/combatspells.c
+++ b/src/spells/combatspells.c
@@ -731,7 +731,7 @@ static fighter *summon_allies(const fighter *fi, const race *rc, int number) {
     unit *u =
         create_unit(r, mage->faction, number, rc, 0, NULL, mage);
     leave(u, true);
-    setstatus(u, ST_FIGHT);
+    unit_setstatus(u, ST_FIGHT);
     
     u->hp = u->number * unit_max_hp(u);
     
@@ -797,7 +797,7 @@ int sp_shadowknights(struct castorder * co)
     u =
         create_unit(r, mage->faction, force, get_race(RC_SHADOWKNIGHT), 0, NULL,
         mage);
-    setstatus(u, ST_FIGHT);
+    unit_setstatus(u, ST_FIGHT);
 
     u->hp = u->number * unit_max_hp(u);
 
@@ -1280,7 +1280,7 @@ int sp_appeasement(struct castorder * co)
     }
     /* und bewachen nicht */
     setguard(mage, false);
-    setstatus(mage, ST_FLEE);
+    unit_setstatus(mage, ST_FLEE);
 
     /* wir tun so, als w�re die Person geflohen */
     fi->flags |= FIG_NOLOOT;
@@ -1624,7 +1624,7 @@ int sp_undeadhero(struct castorder * co)
             else {
                 unit_setinfo(u, NULL);
             }
-            setstatus(u, du->status);
+            unit_setstatus(u, du->status);
             setguard(u, false);
             for (ilist = &du->items; *ilist;) {
                 item *itm = *ilist;

From d15684a546645a92e590df78febc9f80131d4855 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Wed, 14 Feb 2018 20:00:48 +0100
Subject: [PATCH 22/40] fix test_process_turn failure. move turn global to
 calendar. promote calendar module to kernel.

---
 scripts/tests/process.lua        | 24 ++++++++----
 src/CMakeLists.txt               |  2 -
 src/bind_region.c                |  1 +
 src/bindings.c                   | 46 +++++++++++-----------
 src/creport.c                    | 39 ++++++++++---------
 src/economy.c                    | 36 ++++++++---------
 src/eressea.c                    | 39 +++++++++----------
 src/gmtool.c                     | 26 ++++++-------
 src/jsonconf.c                   | 46 +++++++++++-----------
 src/jsonconf.test.c              |  2 +-
 src/kernel/CMakeLists.txt        |  2 +
 src/kernel/alliance.c            | 21 +++++-----
 src/kernel/building.test.c       |  1 +
 src/{ => kernel}/calendar.c      |  1 +
 src/{ => kernel}/calendar.h      |  1 +
 src/{ => kernel}/calendar.test.c |  0
 src/kernel/config.c              |  3 --
 src/kernel/config.h              |  2 -
 src/kernel/faction.c             |  5 ++-
 src/kernel/faction.test.c        |  1 +
 src/kernel/region.c              |  3 +-
 src/kernel/save.c                |  1 +
 src/kernel/unit.c                |  1 +
 src/laws.c                       | 66 ++++++++++++++++----------------
 src/laws.test.c                  |  1 +
 src/main.c                       |  1 +
 src/monsters.c                   | 50 ++++++++++++------------
 src/morale.c                     | 17 ++++----
 src/move.c                       | 43 +++++++++++----------
 src/orderfile.c                  |  9 +++--
 src/orderfile.test.c             |  1 +
 src/report.c                     | 50 ++++++++++++------------
 src/reports.c                    |  2 +-
 src/reports.test.c               |  2 +-
 src/study.c                      |  2 +
 src/summary.c                    |  2 +-
 src/summary.test.c               |  2 +-
 src/tests.c                      |  2 +-
 src/xmlreader.c                  |  2 +-
 39 files changed, 290 insertions(+), 265 deletions(-)
 rename src/{ => kernel}/calendar.c (99%)
 rename src/{ => kernel}/calendar.h (97%)
 rename src/{ => kernel}/calendar.test.c (100%)

diff --git a/scripts/tests/process.lua b/scripts/tests/process.lua
index 0006fcd74..1fc9a64f5 100644
--- a/scripts/tests/process.lua
+++ b/scripts/tests/process.lua
@@ -24,21 +24,31 @@ local function assert_file(filename, exists)
 end
 
 function test_process_turn()
+    turn_begin()
     turn = get_turn()
+    turn_process()
+    turn_end()
+    assert_equal(turn, get_turn())
     turn_begin()
     assert_equal(turn+1, get_turn())
+    turn_process()
+    turn_end()
+end
+
+function test_write_reports()
+    turn_begin()
+    turn = get_turn()
     u:add_order("NUMMER PARTEI 777")
     turn_process()
     assert_equal(0, init_reports())
     assert_equal(0, write_reports())
-    assert_equal(0, eressea.write_game("test.dat"))
-    assert_file("data/test.dat")
-    assert_file("reports/" .. get_turn() .. "-777.nr")
-    assert_file("reports/" .. get_turn() .. "-777.cr")
-    assert_file("reports/" .. get_turn() .. "-777.txt")
+    assert_file("reports/" .. turn .. "-777.nr")
+    assert_file("reports/" .. turn .. "-777.cr")
+    assert_file("reports/" .. turn .. "-777.txt")
     assert_file("reports/reports.txt")
     os.remove("reports")
-    os.remove("data")
+    assert_equal(0, eressea.write_game("test.dat"))
+    assert_file("data/test.dat")
+    os.remove("data/test.dat")
     turn_end()
-    assert_equal(turn+1, get_turn())
 end
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index a74d1d732..3c47385f1 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -86,7 +86,6 @@ ENDIF()
 
 set (ERESSEA_SRC
   vortex.c
-  calendar.c
   move.c
   piracy.c
   spells.c
@@ -198,7 +197,6 @@ set(TESTS_SRC
   academy.test.c
   alchemy.test.c
   battle.test.c
-  calendar.test.c
   creport.test.c
   direction.test.c
   donations.test.c
diff --git a/src/bind_region.c b/src/bind_region.c
index 8514bf639..a87b6f856 100644
--- a/src/bind_region.c
+++ b/src/bind_region.c
@@ -9,6 +9,7 @@
 
 #include "chaos.h"
 
+#include <kernel/calendar.h>
 #include <kernel/config.h>
 #include <kernel/curse.h>
 #include <kernel/region.h>
diff --git a/src/bindings.c b/src/bindings.c
index 811c1853e..35657b5be 100755
--- a/src/bindings.c
+++ b/src/bindings.c
@@ -3,6 +3,28 @@
 #endif
 
 #include "bindings.h"
+
+#include "kernel/calendar.h"
+#include "kernel/config.h"
+#include "kernel/alliance.h"
+#include "kernel/building.h"
+#include "kernel/curse.h"
+#include "kernel/equipment.h"
+#include "kernel/unit.h"
+#include "kernel/terrain.h"
+#include "kernel/messages.h"
+#include "kernel/region.h"
+#include "kernel/building.h"
+#include "kernel/plane.h"
+#include "kernel/race.h"
+#include "kernel/item.h"
+#include "kernel/order.h"
+#include "kernel/ship.h"
+#include "kernel/faction.h"
+#include "kernel/save.h"
+#include "kernel/spell.h"
+#include "kernel/spellbook.h"
+
 #include "bind_unit.h"
 #include "bind_storage.h"
 #include "bind_building.h"
@@ -13,33 +35,11 @@
 #include "bind_ship.h"
 #include "bind_gmtool.h"
 #include "bind_region.h"
+
 #include "helpers.h"
 #include "console.h"
 #include "reports.h"
 #include "study.h"
-#include "calendar.h"
-
-#include <kernel/config.h>
-
-#include <kernel/alliance.h>
-#include <kernel/building.h>
-#include <kernel/curse.h>
-#include <kernel/equipment.h>
-#include <kernel/unit.h>
-#include <kernel/terrain.h>
-#include <kernel/messages.h>
-#include <kernel/region.h>
-#include <kernel/building.h>
-#include <kernel/plane.h>
-#include <kernel/race.h>
-#include <kernel/item.h>
-#include <kernel/order.h>
-#include <kernel/ship.h>
-#include <kernel/faction.h>
-#include <kernel/save.h>
-#include <kernel/spell.h>
-#include <kernel/spellbook.h>
-
 #include "economy.h"
 #include "summary.h"
 #include "teleport.h"
diff --git a/src/creport.c b/src/creport.c
index 04f6c6bc4..773628a82 100644
--- a/src/creport.c
+++ b/src/creport.c
@@ -42,25 +42,26 @@ without prior permission by the authors of Eressea.
 #include "teleport.h"
 
 /* kernel includes */
-#include <kernel/alliance.h>
-#include <kernel/ally.h>
-#include <kernel/connection.h>
-#include <kernel/building.h>
-#include <kernel/curse.h>
-#include <kernel/faction.h>
-#include <kernel/group.h>
-#include <kernel/item.h>
-#include <kernel/messages.h>
-#include <kernel/order.h>
-#include <kernel/plane.h>
-#include <kernel/race.h>
-#include <kernel/region.h>
-#include <kernel/resources.h>
-#include <kernel/ship.h>
-#include <kernel/spell.h>
-#include <kernel/spellbook.h>
-#include <kernel/terrain.h>
-#include <kernel/unit.h>
+#include "kernel/alliance.h"
+#include "kernel/ally.h"
+#include "kernel/calendar.h"
+#include "kernel/connection.h"
+#include "kernel/building.h"
+#include "kernel/curse.h"
+#include "kernel/faction.h"
+#include "kernel/group.h"
+#include "kernel/item.h"
+#include "kernel/messages.h"
+#include "kernel/order.h"
+#include "kernel/plane.h"
+#include "kernel/race.h"
+#include "kernel/region.h"
+#include "kernel/resources.h"
+#include "kernel/ship.h"
+#include "kernel/spell.h"
+#include "kernel/spellbook.h"
+#include "kernel/terrain.h"
+#include "kernel/unit.h"
 
 /* util includes */
 #include <util/attrib.h>
diff --git a/src/economy.c b/src/economy.c
index 219d818f9..9774ab3eb 100644
--- a/src/economy.c
+++ b/src/economy.c
@@ -36,7 +36,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include "monsters.h"
 #include "morale.h"
 #include "reports.h"
-#include "calendar.h"
 
 #include <attributes/reduceproduction.h>
 #include <attributes/racename.h>
@@ -45,23 +44,24 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include <spells/unitcurse.h>
 
 /* kernel includes */
-#include <kernel/ally.h>
-#include <kernel/building.h>
-#include <kernel/curse.h>
-#include <kernel/equipment.h>
-#include <kernel/faction.h>
-#include <kernel/item.h>
-#include <kernel/messages.h>
-#include <kernel/order.h>
-#include <kernel/plane.h>
-#include <kernel/pool.h>
-#include <kernel/race.h>
-#include <kernel/region.h>
-#include <kernel/resources.h>
-#include <kernel/ship.h>
-#include <kernel/terrain.h>
-#include <kernel/terrainid.h>
-#include <kernel/unit.h>
+#include "kernel/ally.h"
+#include "kernel/building.h"
+#include "kernel/calendar.h"
+#include "kernel/curse.h"
+#include "kernel/equipment.h"
+#include "kernel/faction.h"
+#include "kernel/item.h"
+#include "kernel/messages.h"
+#include "kernel/order.h"
+#include "kernel/plane.h"
+#include "kernel/pool.h"
+#include "kernel/race.h"
+#include "kernel/region.h"
+#include "kernel/resources.h"
+#include "kernel/ship.h"
+#include "kernel/terrain.h"
+#include "kernel/terrainid.h"
+#include "kernel/unit.h"
 
 /* util includes */
 #include <util/attrib.h>
diff --git a/src/eressea.c b/src/eressea.c
index 95d190d70..03f194238 100644
--- a/src/eressea.c
+++ b/src/eressea.c
@@ -1,29 +1,28 @@
 #include <platform.h>
-#include "settings.h"
 #include "eressea.h"
 
-#include <kernel/config.h>
-#include <util/log.h>
+#include "kernel/calendar.h"
+#include "kernel/config.h"
+#include "kernel/curse.h"
+#include "kernel/building.h"
+#include "kernel/equipment.h"
+#include "kernel/item.h"
+#include "kernel/database.h"
 
-#include <modules/museum.h>
-#include <triggers/triggers.h>
-#include <util/language.h>
-#include <util/functions.h>
-#include <kernel/building.h>
-#include <kernel/curse.h>
-#include <kernel/equipment.h>
-#include <kernel/item.h>
-#include <kernel/database.h>
-#include <modules/gmcmd.h>
-#include <modules/xmas.h>
-#include <items/xerewards.h>
-#include <items/weapons.h>
+#include "util/functions.h"
+#include "util/language.h"
+#include "util/log.h"
+#include "util/message.h"
 
-#include <attributes/attributes.h>
-#include <util/message.h>
-#include <races/races.h>
+#include "modules/gmcmd.h"
+#include "modules/xmas.h"
+#include "modules/museum.h"
+#include "triggers/triggers.h"
+#include "items/xerewards.h"
+#include "items/weapons.h"
+#include "attributes/attributes.h"
+#include "races/races.h"
 
-#include "calendar.h"
 #include "chaos.h"
 #include "items.h"
 #include "creport.h"
diff --git a/src/gmtool.c b/src/gmtool.c
index 51a86e16c..ffe189611 100644
--- a/src/gmtool.c
+++ b/src/gmtool.c
@@ -23,18 +23,19 @@
 #include <modules/museum.h>
 #include <modules/autoseed.h>
 
-#include <kernel/building.h>
-#include <kernel/faction.h>
-#include <kernel/item.h>
-#include <kernel/plane.h>
-#include <kernel/race.h>
-#include <kernel/region.h>
-#include <kernel/terrainid.h>
-#include <kernel/unit.h>
-#include <kernel/resources.h>
-#include <kernel/save.h>
-#include <kernel/ship.h>
-#include <kernel/terrain.h>
+#include "kernel/building.h"
+#include "kernel/calendar.h"
+#include "kernel/faction.h"
+#include "kernel/item.h"
+#include "kernel/plane.h"
+#include "kernel/race.h"
+#include "kernel/region.h"
+#include "kernel/terrainid.h"
+#include "kernel/unit.h"
+#include "kernel/resources.h"
+#include "kernel/save.h"
+#include "kernel/ship.h"
+#include "kernel/terrain.h"
 
 #include <attributes/attributes.h>
 #include <triggers/triggers.h>
@@ -53,7 +54,6 @@
 #include "console.h"
 #include "listbox.h"
 #include "wormhole.h"
-#include "calendar.h"
 #include "teleport.h"
 #include "xmlreader.h"
 
diff --git a/src/jsonconf.c b/src/jsonconf.c
index ed10f558f..c14ac9216 100644
--- a/src/jsonconf.c
+++ b/src/jsonconf.c
@@ -11,36 +11,36 @@ without prior permission by the authors of Eressea.
 */
 
 #include <platform.h>
-#include <kernel/config.h>
 #include "jsonconf.h"
 
 /* kernel includes */
-#include <kernel/building.h>
-#include <kernel/equipment.h>
-#include <kernel/item.h>
-#include <kernel/messages.h>
-#include <kernel/race.h>
-#include <kernel/region.h>
-#include <kernel/resources.h>
-#include <kernel/ship.h>
-#include <kernel/terrain.h>
-#include <kernel/spell.h>
-#include <kernel/spellbook.h>
+#include "kernel/building.h"
+#include "kernel/calendar.h"
+#include "kernel/config.h"
+#include "kernel/equipment.h"
+#include "kernel/item.h"
+#include "kernel/messages.h"
+#include "kernel/race.h"
+#include "kernel/region.h"
+#include "kernel/resources.h"
+#include "kernel/ship.h"
+#include "kernel/terrain.h"
+#include "kernel/spell.h"
+#include "kernel/spellbook.h"
 
 /* util includes */
-#include <util/attrib.h>
-#include <util/crmessage.h>
-#include <util/functions.h>
-#include <util/language.h>
-#include <util/log.h>
-#include <util/message.h>
-#include <util/nrmessage.h>
-#include <util/path.h>
-#include <util/strings.h>
-#include <util/xml.h>
+#include "util/attrib.h"
+#include "util/crmessage.h"
+#include "util/functions.h"
+#include "util/language.h"
+#include "util/log.h"
+#include "util/message.h"
+#include "util/nrmessage.h"
+#include "util/path.h"
+#include "util/strings.h"
+#include "util/xml.h"
 
 /* game modules */
-#include "calendar.h"
 #include "direction.h"
 #include "keyword.h"
 #include "move.h"
diff --git a/src/jsonconf.test.c b/src/jsonconf.test.c
index 29c68e600..88630c833 100644
--- a/src/jsonconf.test.c
+++ b/src/jsonconf.test.c
@@ -17,7 +17,7 @@
 
 #include "util/language.h"
 
-#include "calendar.h"
+#include "kernel/calendar.h"
 #include "direction.h"
 #include "keyword.h"
 #include "move.h"
diff --git a/src/kernel/CMakeLists.txt b/src/kernel/CMakeLists.txt
index cb8373fa2..59b8cb806 100644
--- a/src/kernel/CMakeLists.txt
+++ b/src/kernel/CMakeLists.txt
@@ -7,6 +7,7 @@ ally.test.c
 build.test.c
 building.test.c
 # callbacks.test.c
+calendar.test.c
 command.test.c
 config.test.c
 # connection.test.c
@@ -49,6 +50,7 @@ ally.c
 build.c
 building.c
 callbacks.c
+calendar.c
 command.c
 config.c
 connection.c
diff --git a/src/kernel/alliance.c b/src/kernel/alliance.c
index 1aa3edd95..d5dda0143 100644
--- a/src/kernel/alliance.c
+++ b/src/kernel/alliance.c
@@ -11,19 +11,20 @@ without prior permission by the authors of Eressea.
 */
 
 #include <platform.h>
-#include <kernel/config.h>
 #include "alliance.h"
 
-#include <attributes/key.h>
-
 /* kernel includes */
-#include <kernel/building.h>
-#include <kernel/faction.h>
-#include <kernel/messages.h>
-#include <kernel/order.h>
-#include <kernel/region.h>
-#include <kernel/unit.h>
-#include <kernel/command.h>
+#include "calendar.h"
+#include "config.h"
+#include "building.h"
+#include "faction.h"
+#include "messages.h"
+#include "order.h"
+#include "region.h"
+#include "unit.h"
+#include "command.h"
+
+#include <attributes/key.h>
 
 /* util includes */
 #include <util/attrib.h>
diff --git a/src/kernel/building.test.c b/src/kernel/building.test.c
index 8d5b86dc9..9c7013db3 100644
--- a/src/kernel/building.test.c
+++ b/src/kernel/building.test.c
@@ -1,5 +1,6 @@
 #include <platform.h>
 
+#include <kernel/calendar.h>
 #include <kernel/config.h>
 #include <kernel/race.h>
 #include <kernel/region.h>
diff --git a/src/calendar.c b/src/kernel/calendar.c
similarity index 99%
rename from src/calendar.c
rename to src/kernel/calendar.c
index 23562daa6..df9f6c204 100644
--- a/src/calendar.c
+++ b/src/kernel/calendar.c
@@ -10,6 +10,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 
+int turn = 0;
 int first_month = 0;
 int weeks_per_month = 3;
 int months_per_year = 12;
diff --git a/src/calendar.h b/src/kernel/calendar.h
similarity index 97%
rename from src/calendar.h
rename to src/kernel/calendar.h
index af4bd6a4f..164a96bf4 100644
--- a/src/calendar.h
+++ b/src/kernel/calendar.h
@@ -17,6 +17,7 @@ extern "C" {
     extern int months_per_year;
     extern int *month_season;
     extern int first_month;
+    extern int turn;
 
     extern char **weeknames;
     extern char **weeknames2;
diff --git a/src/calendar.test.c b/src/kernel/calendar.test.c
similarity index 100%
rename from src/calendar.test.c
rename to src/kernel/calendar.test.c
diff --git a/src/kernel/config.c b/src/kernel/config.c
index 2895ed70a..05690e301 100644
--- a/src/kernel/config.c
+++ b/src/kernel/config.c
@@ -29,7 +29,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include "curse.h"
 #include "connection.h"
 #include "building.h"
-#include "calendar.h"
 #include "direction.h"
 #include "equipment.h"
 #include "faction.h"
@@ -98,8 +97,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #endif
 struct settings global;
 
-int turn = 0;
-
 const char *parameters[MAXPARAMS] = {
     "LOCALE",
     "ALLES",
diff --git a/src/kernel/config.h b/src/kernel/config.h
index e0ad682c6..94abcb7f4 100644
--- a/src/kernel/config.h
+++ b/src/kernel/config.h
@@ -141,8 +141,6 @@ extern "C" {
     extern const char *parameters[];
     extern settings global;
 
-    extern int turn;
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/kernel/faction.c b/src/kernel/faction.c
index 90649600f..36f467c6e 100755
--- a/src/kernel/faction.c
+++ b/src/kernel/faction.c
@@ -17,8 +17,11 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 **/
 
 #include <platform.h>
-#include <kernel/config.h>
+
 #include "faction.h"
+
+#include "calendar.h"
+#include "config.h"
 #include "alliance.h"
 #include "ally.h"
 #include "curse.h"
diff --git a/src/kernel/faction.test.c b/src/kernel/faction.test.c
index 904609be5..18a64ee90 100644
--- a/src/kernel/faction.test.c
+++ b/src/kernel/faction.test.c
@@ -2,6 +2,7 @@
 
 #include <kernel/ally.h>
 #include <kernel/alliance.h>
+#include <kernel/calendar.h>
 #include <kernel/faction.h>
 #include <kernel/item.h>
 #include <kernel/plane.h>
diff --git a/src/kernel/region.c b/src/kernel/region.c
index 28d4e8bee..40b6ad484 100644
--- a/src/kernel/region.c
+++ b/src/kernel/region.c
@@ -17,12 +17,13 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 **/
 
 #include <platform.h>
-#include <kernel/config.h>
 #include "region.h"
 
 /* kernel includes */
 #include "alliance.h"
 #include "building.h"
+#include "calendar.h"
+#include "config.h"
 #include "connection.h"
 #include "curse.h"
 #include "equipment.h"
diff --git a/src/kernel/save.c b/src/kernel/save.c
index a4bfdcbca..3dce7185e 100644
--- a/src/kernel/save.c
+++ b/src/kernel/save.c
@@ -25,6 +25,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include "alliance.h"
 #include "ally.h"
 #include "building.h"
+#include "calendar.h"
 #include "connection.h"
 #include "equipment.h"
 #include "faction.h"
diff --git a/src/kernel/unit.c b/src/kernel/unit.c
index acc28fff3..037ca7c06 100644
--- a/src/kernel/unit.c
+++ b/src/kernel/unit.c
@@ -22,6 +22,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 #include "ally.h"
 #include "building.h"
+#include "calendar.h"
 #include "faction.h"
 #include "group.h"
 #include "connection.h"
diff --git a/src/laws.c b/src/laws.c
index a31d67589..739b6a0a4 100644
--- a/src/laws.c
+++ b/src/laws.c
@@ -41,43 +41,33 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include "prefix.h"
 #include "reports.h"
 #include "teleport.h"
-#include "calendar.h"
 #include "guard.h"
 #include "volcano.h"
 
-/* attributes includes */
-#include <attributes/racename.h>
-#include <attributes/raceprefix.h>
-#include <attributes/seenspell.h>
-#include <attributes/stealth.h>
-
-#include <spells/buildingcurse.h>
-#include <spells/regioncurse.h>
-#include <spells/unitcurse.h>
-
 /* kernel includes */
-#include <kernel/alliance.h>
-#include <kernel/ally.h>
-#include <kernel/callbacks.h>
-#include <kernel/connection.h>
-#include <kernel/curse.h>
-#include <kernel/building.h>
-#include <kernel/faction.h>
-#include <kernel/group.h>
-#include <kernel/item.h>
-#include <kernel/messages.h>
-#include <kernel/order.h>
-#include <kernel/plane.h>
-#include <kernel/pool.h>
-#include <kernel/race.h>
-#include <kernel/region.h>
-#include <kernel/resources.h>
-#include <kernel/ship.h>
-#include <kernel/spell.h>
-#include <kernel/spellbook.h>
-#include <kernel/terrain.h>
-#include <kernel/terrainid.h>
-#include <kernel/unit.h>
+#include "kernel/alliance.h"
+#include "kernel/ally.h"
+#include "kernel/calendar.h"
+#include "kernel/callbacks.h"
+#include "kernel/connection.h"
+#include "kernel/curse.h"
+#include "kernel/building.h"
+#include "kernel/faction.h"
+#include "kernel/group.h"
+#include "kernel/item.h"
+#include "kernel/messages.h"
+#include "kernel/order.h"
+#include "kernel/plane.h"
+#include "kernel/pool.h"
+#include "kernel/race.h"
+#include "kernel/region.h"
+#include "kernel/resources.h"
+#include "kernel/ship.h"
+#include "kernel/spell.h"
+#include "kernel/spellbook.h"
+#include "kernel/terrain.h"
+#include "kernel/terrainid.h"
+#include "kernel/unit.h"
 
 /* util includes */
 #include <util/attrib.h>
@@ -98,10 +88,20 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include <util/umlaut.h>
 #include <util/unicode.h>
 
+/* attributes includes */
 #include <attributes/otherfaction.h>
+#include <attributes/racename.h>
+#include <attributes/raceprefix.h>
+#include <attributes/seenspell.h>
+#include <attributes/stealth.h>
+
+#include <spells/buildingcurse.h>
+#include <spells/regioncurse.h>
+#include <spells/unitcurse.h>
 
 #include <selist.h>
 #include <iniparser.h>
+
 /* libc includes */
 #include <assert.h>
 #include <stdio.h>
diff --git a/src/laws.test.c b/src/laws.test.c
index 5edde187d..888614a1a 100644
--- a/src/laws.test.c
+++ b/src/laws.test.c
@@ -6,6 +6,7 @@
 
 #include <kernel/ally.h>
 #include <kernel/alliance.h>
+#include <kernel/calendar.h>
 #include <kernel/config.h>
 #include <kernel/building.h>
 #include <kernel/faction.h>
diff --git a/src/main.c b/src/main.c
index f50f65472..ae6d2158c 100644
--- a/src/main.c
+++ b/src/main.c
@@ -20,6 +20,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include <platform.h>
 #endif
 
+#include <kernel/calendar.h>
 #include <kernel/config.h>
 #include <kernel/database.h>
 #include <kernel/messages.h>
diff --git a/src/monsters.c b/src/monsters.c
index d1979414d..97b29d2cc 100644
--- a/src/monsters.c
+++ b/src/monsters.c
@@ -18,7 +18,6 @@
  */
 
 #include <platform.h>
-#include <kernel/config.h>
 
 #include "monsters.h"
 
@@ -29,32 +28,27 @@
 #include "laws.h"
 #include "keyword.h"
 #include "study.h"
-
-/* attributes includes */
-#include <attributes/targetregion.h>
-#include <attributes/hate.h>
-
-#include <spells/regioncurse.h>
+#include "move.h"
 
 /* kernel includes */
-#include <kernel/build.h>
-#include <kernel/building.h>
-#include <kernel/curse.h>
-#include <kernel/equipment.h>
-#include <kernel/faction.h>
-#include <kernel/item.h>
-#include <kernel/messages.h>
-#include <kernel/order.h>
-#include <kernel/pathfinder.h>
-#include <kernel/pool.h>
-#include <kernel/race.h>
-#include <kernel/region.h>
-#include <kernel/ship.h>
-#include <kernel/terrain.h>
-#include <kernel/terrainid.h>
-#include <kernel/unit.h>
-
-#include <move.h>
+#include "kernel/build.h"
+#include "kernel/building.h"
+#include "kernel/calendar.h"
+#include "kernel/config.h"
+#include "kernel/curse.h"
+#include "kernel/equipment.h"
+#include "kernel/faction.h"
+#include "kernel/item.h"
+#include "kernel/messages.h"
+#include "kernel/order.h"
+#include "kernel/pathfinder.h"
+#include "kernel/pool.h"
+#include "kernel/race.h"
+#include "kernel/region.h"
+#include "kernel/ship.h"
+#include "kernel/terrain.h"
+#include "kernel/terrainid.h"
+#include "kernel/unit.h"
 
 /* util includes */
 #include <util/attrib.h>
@@ -68,6 +62,12 @@
 #include <util/rng.h>
 #include <util/strings.h>
 
+/* attributes includes */
+#include <attributes/targetregion.h>
+#include <attributes/hate.h>
+
+#include <spells/regioncurse.h>
+
 #include <selist.h>
 
 /* libc includes */
diff --git a/src/morale.c b/src/morale.c
index a8a7cc9d9..6155426ac 100644
--- a/src/morale.c
+++ b/src/morale.c
@@ -20,17 +20,18 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include <platform.h>
 #include "morale.h"
 
-#include <spells/regioncurse.h>
-
-#include <kernel/config.h>
-#include <kernel/curse.h>
-#include <kernel/region.h>
-#include <kernel/faction.h>
-#include <kernel/race.h>
-#include <kernel/building.h>
+#include "kernel/calendar.h"
+#include "kernel/config.h"
+#include "kernel/curse.h"
+#include "kernel/region.h"
+#include "kernel/faction.h"
+#include "kernel/race.h"
+#include "kernel/building.h"
 
 #include <util/rand.h>
 
+#include <spells/regioncurse.h>
+
 #include <assert.h>
 
 static double popularity(void)
diff --git a/src/move.c b/src/move.c
index 72d311395..00a787cac 100644
--- a/src/move.c
+++ b/src/move.c
@@ -20,7 +20,28 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #ifdef _MSC_VER
 #include <platform.h>
 #endif
-#include <kernel/config.h>
+
+/* kernel includes */
+#include "kernel/ally.h"
+#include "kernel/build.h"
+#include "kernel/building.h"
+#include "kernel/calendar.h"
+#include "kernel/config.h"
+#include "kernel/connection.h"
+#include "kernel/curse.h"
+#include "kernel/faction.h"
+#include "kernel/item.h"
+#include "kernel/messages.h"
+#include "kernel/order.h"
+#include "kernel/plane.h"
+#include "kernel/race.h"
+#include "kernel/region.h"
+#include "kernel/render.h"
+#include "kernel/ship.h"
+#include "kernel/terrain.h"
+#include "kernel/terrainid.h"
+#include "kernel/unit.h"
+
 #include "move.h"
 #include "guard.h"
 #include "laws.h"
@@ -44,28 +65,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include <attributes/stealth.h>
 #include <attributes/targetregion.h>
 
-/* kernel includes */
-#include <kernel/ally.h>
-#include <kernel/build.h>
-#include <kernel/building.h>
-#include <kernel/connection.h>
-#include <kernel/curse.h>
-#include <kernel/faction.h>
-#include <kernel/item.h>
-#include <kernel/messages.h>
-#include <kernel/order.h>
-#include <kernel/plane.h>
-#include <kernel/race.h>
-#include <kernel/region.h>
-#include <kernel/render.h>
-#include <kernel/ship.h>
-#include <kernel/terrain.h>
-#include <kernel/terrainid.h>
-#include <kernel/unit.h>
-
 #include "teleport.h"
 #include "direction.h"
-#include "calendar.h"
 #include "skill.h"
 
 /* util includes */
diff --git a/src/orderfile.c b/src/orderfile.c
index a82b599a0..ed4860d77 100644
--- a/src/orderfile.c
+++ b/src/orderfile.c
@@ -2,10 +2,11 @@
 #include <kernel/config.h>
 #include "orderfile.h"
 
-#include <kernel/faction.h>
-#include <kernel/unit.h>
-#include <kernel/order.h>
-#include <kernel/messages.h>
+#include "kernel/calendar.h"
+#include "kernel/faction.h"
+#include "kernel/messages.h"
+#include "kernel/order.h"
+#include "kernel/unit.h"
 
 #include <util/base36.h>
 #include <util/message.h>
diff --git a/src/orderfile.test.c b/src/orderfile.test.c
index 500c68fdf..f5d4997ae 100644
--- a/src/orderfile.test.c
+++ b/src/orderfile.test.c
@@ -3,6 +3,7 @@
 
 #include "orderfile.h"
 
+#include <kernel/calendar.h>
 #include <kernel/faction.h>
 #include <util/message.h>
 
diff --git a/src/report.c b/src/report.c
index 9e98e5162..44b1bd6ad 100644
--- a/src/report.c
+++ b/src/report.c
@@ -43,7 +43,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 /* gamecode includes */
 #include "alchemy.h"
-#include "calendar.h"
 #include "economy.h"
 #include "move.h"
 #include "upkeep.h"
@@ -51,30 +50,31 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include "teleport.h"
 
 /* kernel includes */
-#include <kernel/ally.h>
-#include <kernel/connection.h>
-#include <kernel/build.h>
-#include <kernel/building.h>
-#include <kernel/curse.h>
-#include <kernel/faction.h>
-#include <kernel/group.h>
-#include <kernel/item.h>
-#include <kernel/messages.h>
-#include <kernel/objtypes.h>
-#include <kernel/order.h>
-#include <kernel/plane.h>
-#include <kernel/pool.h>
-#include <kernel/race.h>
-#include <kernel/region.h>
-#include <kernel/render.h>
-#include <kernel/resources.h>
-#include <kernel/ship.h>
-#include <kernel/spell.h>
-#include <kernel/spellbook.h>
-#include <kernel/terrain.h>
-#include <kernel/terrainid.h>
-#include <kernel/unit.h>
-#include <kernel/alliance.h>
+#include "kernel/ally.h"
+#include "kernel/calendar.h"
+#include "kernel/connection.h"
+#include "kernel/build.h"
+#include "kernel/building.h"
+#include "kernel/curse.h"
+#include "kernel/faction.h"
+#include "kernel/group.h"
+#include "kernel/item.h"
+#include "kernel/messages.h"
+#include "kernel/objtypes.h"
+#include "kernel/order.h"
+#include "kernel/plane.h"
+#include "kernel/pool.h"
+#include "kernel/race.h"
+#include "kernel/region.h"
+#include "kernel/render.h"
+#include "kernel/resources.h"
+#include "kernel/ship.h"
+#include "kernel/spell.h"
+#include "kernel/spellbook.h"
+#include "kernel/terrain.h"
+#include "kernel/terrainid.h"
+#include "kernel/unit.h"
+#include "kernel/alliance.h"
 
 /* util includes */
 #include <util/attrib.h>
diff --git a/src/reports.c b/src/reports.c
index d4be5c8ce..20f2068e8 100644
--- a/src/reports.c
+++ b/src/reports.c
@@ -21,7 +21,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include "reports.h"
 
 #include "battle.h"
-#include "calendar.h"
+#include "kernel/calendar.h"
 #include "guard.h"
 #include "laws.h"
 #include "spells.h"
diff --git a/src/reports.test.c b/src/reports.test.c
index ca0f98f57..cdc09c83c 100644
--- a/src/reports.test.c
+++ b/src/reports.test.c
@@ -1,7 +1,7 @@
 #include <platform.h>
 #include "reports.h"
 
-#include "calendar.h"
+#include "kernel/calendar.h"
 #include "keyword.h"
 #include "lighthouse.h"
 #include "laws.h"
diff --git a/src/study.c b/src/study.c
index 1dbc890ed..46da90b74 100644
--- a/src/study.c
+++ b/src/study.c
@@ -21,11 +21,13 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #endif
 #include <kernel/config.h>
 #include "study.h"
+
 #include "laws.h"
 #include "move.h"
 #include "monsters.h"
 #include "alchemy.h"
 #include "academy.h"
+#include "kernel/calendar.h"
 
 #include <spells/regioncurse.h>
 
diff --git a/src/summary.c b/src/summary.c
index ee047bc8f..64bb055b5 100644
--- a/src/summary.c
+++ b/src/summary.c
@@ -16,7 +16,7 @@
 #include "summary.h"
 #include "laws.h"
 #include "monsters.h"
-#include "calendar.h"
+#include "kernel/calendar.h"
 
 #include <kernel/alliance.h>
 #include <kernel/faction.h>
diff --git a/src/summary.test.c b/src/summary.test.c
index 5df4849c3..7b7df93b6 100644
--- a/src/summary.test.c
+++ b/src/summary.test.c
@@ -1,7 +1,7 @@
 #include <platform.h>
 
 #include "summary.h"
-#include "calendar.h"
+#include "kernel/calendar.h"
 
 #include <CuTest.h>
 #include "tests.h"
diff --git a/src/tests.c b/src/tests.c
index 18ecdef34..4b764b23f 100644
--- a/src/tests.c
+++ b/src/tests.c
@@ -3,7 +3,7 @@
 #include "keyword.h"
 #include "prefix.h"
 #include "reports.h"
-#include "calendar.h"
+#include "kernel/calendar.h"
 #include "vortex.h"
 
 #include <kernel/config.h>
diff --git a/src/xmlreader.c b/src/xmlreader.c
index a711b7dbd..66b04e611 100644
--- a/src/xmlreader.c
+++ b/src/xmlreader.c
@@ -29,7 +29,7 @@ without prior permission by the authors of Eressea.
 #include "kernel/spellbook.h"
 
 #include "alchemy.h"
-#include "calendar.h"
+#include "kernel/calendar.h"
 #include "guard.h"
 #include "keyword.h"
 #include "move.h"

From 4abc603d9db6c7aa5f1b0411f365a5dc3421ad40 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Wed, 14 Feb 2018 20:02:50 +0100
Subject: [PATCH 23/40] triggered a missing forward declaration.

---
 src/economy.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/economy.h b/src/economy.h
index ec12ae0ca..6d58a1f9f 100644
--- a/src/economy.h
+++ b/src/economy.h
@@ -45,6 +45,7 @@ extern "C" {
     struct faction;
     struct order;
     struct message;
+    struct item_type;
     
     typedef struct econ_request {
         struct econ_request *next;

From bf591ecec5cecd5c5350fad13670dd3d27728eb5 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Thu, 15 Feb 2018 20:25:58 +0100
Subject: [PATCH 24/40] BUG 2415: eliminate at_potiondelay and WdL delay. BUG
 2419: refactor ointment and healing, add tests for USE

---
 scripts/tests/e2/items.lua      | 59 ++++++++++++++++++++++++++++-----
 scripts/tests/e3/production.lua |  1 -
 src/alchemy.c                   | 58 ++------------------------------
 src/alchemy.h                   |  4 +--
 src/battle.h                    |  1 +
 src/creport.h                   |  1 +
 src/items.c                     | 16 ++++-----
 src/kernel/building.h           |  1 +
 src/kernel/region.test.c        |  2 +-
 src/kernel/save.c               |  2 +-
 src/kernel/types.h              |  8 ++---
 src/magic.h                     |  1 +
 src/volcano.h                   |  3 ++
 13 files changed, 74 insertions(+), 83 deletions(-)

diff --git a/scripts/tests/e2/items.lua b/scripts/tests/e2/items.lua
index 2f2b13e88..fe3bca370 100644
--- a/scripts/tests/e2/items.lua
+++ b/scripts/tests/e2/items.lua
@@ -28,7 +28,7 @@ end
 
 function test_nestwarmth_insect()
     local r = region.create(0, 0, "plain")
-    local f = faction.create("insect", "noreply@eressea.de", "de")
+    local f = faction.create("insect")
     local u = unit.create(f, r, 1)
     local flags = u.flags
     u:add_item("nestwarmth", 2)
@@ -44,7 +44,7 @@ end
 
 function test_nestwarmth_other()
     local r = region.create(0, 0, "plain")
-    local f = faction.create("human", "noreply@eressea.de", "de")
+    local f = faction.create("human")
     local u = unit.create(f, r, 1)
     local flags = u.flags
     u:add_item("nestwarmth", 2)
@@ -60,7 +60,7 @@ end
 
 function test_meow()
     local r = region.create(0, 0, "plain")
-    local f = faction.create("human", "noreply@eressea.de", "de")
+    local f = faction.create("human")
     local u = unit.create(f, r, 1)
     u:add_item("aoc", 1)
     u:clear_orders()
@@ -74,7 +74,7 @@ end
 
 function test_aurapotion50()
     local r = region.create(0, 0, "plain")
-    local f = faction.create("human", "noreply@eressea.de", "de")
+    local f = faction.create("human")
     local u = unit.create(f, r, 1)
     u:add_item("aurapotion50", 1)
     u:set_skill('magic', 10);
@@ -92,7 +92,7 @@ end
 
 function test_bagpipe()
     local r = region.create(0, 0, "plain")
-    local f = faction.create("human", "noreply@eressea.de", "de")
+    local f = faction.create("human")
     local u = unit.create(f, r, 1)
     turn_begin()
     u:add_item("bagpipeoffear", 1)
@@ -109,9 +109,52 @@ function test_bagpipe()
     assert_equal(0, r:get_curse('depression'))
 end
 
+function test_monthly_healing()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("human")
+    local u = unit.create(f, r, 30)
+    assert_equal(600, u.hp)
+    u.hp = 100
+    process_orders()
+    assert_equal(130, u.hp)
+end
+
+function test_ointment()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("human")
+    local u = unit.create(f, r, 30)
+    assert_equal(600, u.hp)
+    u.hp = 100
+    turn_begin()
+    u:add_item("ointment", 1)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Wundsalbe")
+    turn_process()
+    assert_equal(530, u.hp)
+    assert_equal(0, u:get_item("ointment"))
+    turn_end()
+end
+
+function test_use_healing_potion()
+    -- Heiltrank kann (auch) mit BENUTZE eingesetzt werden
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("human")
+    local u = unit.create(f, r, 30)
+    assert_equal(600, u.hp)
+    u.hp = 100
+    turn_begin()
+    u:add_item("p14", 1)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Heiltrank")
+    turn_process()
+    assert_equal(530, u.hp)
+    assert_equal(0, u:get_item("p14"))
+    turn_end()
+end
+
 function test_speedsail()
     local r = region.create(0, 0, "plain")
-    local f = faction.create("human", "noreply@eressea.de", "de")
+    local f = faction.create("human")
     local u = unit.create(f, r, 1)
     
     turn_begin()
@@ -130,7 +173,7 @@ end
 
 function disable_test_foolpotion()
     local r = region.create(0, 0, "plain")
-    local f = faction.create("human", "noreply@eressea.de", "de")
+    local f = faction.create("human")
     local u = unit.create(f, r, 1)
     turn_begin()
     u:add_item('p7', 2)
@@ -159,7 +202,7 @@ end
 
 function test_snowman()
     local r = region.create(0, 0, "glacier")
-    local f = faction.create("human", "noreply@eressea.de", "de")
+    local f = faction.create("human")
     local u = unit.create(f, r, 1)
     u:add_item("snowman", 1)
     u:clear_orders()
diff --git a/scripts/tests/e3/production.lua b/scripts/tests/e3/production.lua
index be63158cf..3469b06c4 100644
--- a/scripts/tests/e3/production.lua
+++ b/scripts/tests/e3/production.lua
@@ -53,7 +53,6 @@ function test_dwarf_no_mining_bonus()
     local r = region.create(0, 0, 'mountain')
     local f = create_faction('dwarf')
     local u = unit.create(f, r, 1)
-    u.name = 'Xolgrim'
 
     turn_begin()
     r:set_resource('iron', 100)
diff --git a/src/alchemy.c b/src/alchemy.c
index 3ec21159d..b766278c6 100644
--- a/src/alchemy.c
+++ b/src/alchemy.c
@@ -211,7 +211,7 @@ void show_potions(faction *f, int sklevel)
     }
 }
 
-static int potion_healing(unit * u, int amount) {
+static int potion_ointment(unit * u, int amount) {
     int maxhp = unit_max_hp(u) * u->number;
     u->hp = u->hp + 400 * amount;
     if (u->hp > maxhp) u->hp = maxhp;
@@ -244,8 +244,8 @@ static int do_potion(unit * u, region *r, const item_type * itype, int amount)
     if (itype == oldpotiontype[P_LIFE]) {
         return potion_water_of_life(u, r, amount);
     }
-    else if (itype == oldpotiontype[P_HEILWASSER]) {
-        return potion_healing(u, amount);
+    else if (itype == oldpotiontype[P_OINTMENT]) {
+        return potion_ointment(u, amount);
     }
     else if (itype == oldpotiontype[P_PEOPLE]) {
         return potion_luck(u, r, &at_peasantluck, amount);
@@ -277,58 +277,6 @@ int use_potion(unit * u, const item_type * itype, int amount, struct order *ord)
     return 0;
 }
 
-typedef struct potiondelay {
-    unit *u;
-    region *r;
-    const item_type *itype;
-    int amount;
-} potiondelay;
-
-static void init_potiondelay(variant *var)
-{
-    var->v = malloc(sizeof(potiondelay));
-}
-
-static int age_potiondelay(attrib * a, void *owner)
-{
-    potiondelay *pd = (potiondelay *)a->data.v;
-    UNUSED_ARG(owner);
-    pd->amount = do_potion(pd->u, pd->r, pd->itype, pd->amount);
-    return AT_AGE_REMOVE;
-}
-
-attrib_type at_potiondelay = {
-    "potiondelay",
-    init_potiondelay,
-    a_free_voidptr,
-    age_potiondelay, 0, 0
-};
-
-static attrib *make_potiondelay(unit * u, const item_type * itype, int amount)
-{
-    attrib *a = a_new(&at_potiondelay);
-    potiondelay *pd = (potiondelay *)a->data.v;
-    pd->u = u;
-    pd->r = u->region;
-    pd->itype = itype;
-    pd->amount = amount;
-    return a;
-}
-
-int
-use_potion_delayed(unit * u, const item_type * itype, int amount,
-struct order *ord)
-{
-    int result = begin_potion(u, itype, ord);
-    if (result)
-        return result;
-
-    a_add(&u->attribs, make_potiondelay(u, itype, amount));
-
-    end_potion(u, itype, amount);
-    return 0;
-}
-
 /*****************/
 /*   at_effect   */
 /*****************/
diff --git a/src/alchemy.h b/src/alchemy.h
index d183cacd0..3ce6b9547 100644
--- a/src/alchemy.h
+++ b/src/alchemy.h
@@ -41,7 +41,7 @@ extern "C" {
         P_LIFE,
         /* Stufe 2 */
         P_DOMORE,
-        P_HEILWASSER,
+        P_OINTMENT,
         P_BAUERNBLUT,
         /* Stufe 3 */
         P_WISE,                     /* 6 */
@@ -64,8 +64,6 @@ extern "C" {
     void herbsearch(struct unit *u, int max);
     int use_potion(struct unit *u, const struct item_type *itype,
         int amount, struct order *);
-    int use_potion_delayed(struct unit *u, const struct item_type *itype,
-        int amount, struct order *);
 
     int get_effect(const struct unit *u, const struct item_type *effect);
     int change_effect(struct unit *u, const struct item_type *effect,
diff --git a/src/battle.h b/src/battle.h
index 6494a6e27..fd31a0284 100644
--- a/src/battle.h
+++ b/src/battle.h
@@ -28,6 +28,7 @@ extern "C" {
 
     struct message;
     struct selist;
+    union variant;
 
     /** more defines **/
 #define FS_ENEMY 1
diff --git a/src/creport.h b/src/creport.h
index ed8f3bfbd..f4e0cd4bd 100644
--- a/src/creport.h
+++ b/src/creport.h
@@ -13,6 +13,7 @@
 #define H_GC_CREPORT
 
 #include <kernel/types.h>
+#include <stdbool.h>
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/src/items.c b/src/items.c
index ba9f35388..f1940a644 100644
--- a/src/items.c
+++ b/src/items.c
@@ -16,6 +16,7 @@
 #include <spells/regioncurse.h>
 
 #include <kernel/curse.h>
+#include <kernel/config.h>
 #include <kernel/faction.h>
 #include <kernel/item.h>
 #include <kernel/messages.h>
@@ -321,8 +322,8 @@ static int heal(unit * user, int effect)
 }
 
 static int
-use_healingpotion(struct unit *user, const struct item_type *itype, int amount,
-struct order *ord)
+use_healingpotion(struct unit *user, const struct item_type *itype,
+    int amount, struct order *ord)
 {
     int effect = amount * 400;
     unit *u = user->region->units;
@@ -400,8 +401,7 @@ static int use_warmthpotion(unit *u, const item_type *itype,
         cmistake(u, ord, 163, MSG_EVENT);
         return ECUSTOM;
     }
-    use_pooled(u, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK,
-        amount);
+    use_pooled(u, itype->rtype, GET_DEFAULT, amount);
     usetpotionuse(u, itype);
 
     ADDMSG(&u->faction->msgs, msg_message("usepotion",
@@ -422,9 +422,9 @@ void register_itemfunctions(void)
     register_item_use(use_birthdayamulet, "use_aoc");
     register_item_use(use_foolpotion, "use_p7");
     register_item_use(use_bloodpotion, "use_peasantblood");
-    register_item_use(use_healingpotion, "use_ointment");
+    register_item_use(use_potion, "use_ointment");
+    register_item_use(use_healingpotion, "use_p14");
     register_item_use(use_warmthpotion, "use_nestwarmth");
-
-    /* ungetestet: Wasser des Lebens */
-    register_item_use(use_potion_delayed, "use_p2");
+    /* p2 = P_LIFE = Wasser des Lebens */
+    register_item_use(use_potion, "use_p2");
 }
diff --git a/src/kernel/building.h b/src/kernel/building.h
index e162e041f..5a83fd0ca 100644
--- a/src/kernel/building.h
+++ b/src/kernel/building.h
@@ -21,6 +21,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 #include <kernel/types.h>
 #include <util/resolve.h>
+#include <util/variant.h>
 
 #include <stddef.h>
 #include <stdbool.h>
diff --git a/src/kernel/region.test.c b/src/kernel/region.test.c
index 4baa807c8..d54049b8e 100644
--- a/src/kernel/region.test.c
+++ b/src/kernel/region.test.c
@@ -16,7 +16,7 @@ void test_terraform(CuTest *tc) {
     item_type *itype;
 
     test_setup();
-    itype = test_create_itemtype("ointment");
+    itype = test_create_itemtype("oil");
     itype->rtype->flags |= (RTF_ITEM | RTF_POOLED);
     new_luxurytype(itype, 0);
 
diff --git a/src/kernel/save.c b/src/kernel/save.c
index 1d415038d..4408ab909 100644
--- a/src/kernel/save.c
+++ b/src/kernel/save.c
@@ -496,7 +496,7 @@ unit *read_unit(gamedata *data)
     }
 
     READ_INT(data->store, &n);
-    unit_setstatus(u, n);
+    unit_setstatus(u, (status_t)n);
     READ_INT(data->store, &u->flags);
     u->flags &= UFL_SAVEMASK;
     if ((u->flags & UFL_ANON_FACTION) && !rule_stealth_anon()) {
diff --git a/src/kernel/types.h b/src/kernel/types.h
index 9e9ed3b66..61f1268cc 100644
--- a/src/kernel/types.h
+++ b/src/kernel/types.h
@@ -20,9 +20,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #define ERESSEA_TYPES_H
 
 #include <settings.h>
-#include <util/variant.h>
-
-typedef short item_t;
 
 struct attrib;
 struct attrib_type;
@@ -77,15 +74,14 @@ typedef enum {
 
 /* ------------------ Status von Einheiten --------------------- */
 
-typedef unsigned char status_t;
-enum {
+typedef enum {
   ST_AGGRO,
   ST_FIGHT,
   ST_BEHIND,
   ST_CHICKEN,
   ST_AVOID,
   ST_FLEE
-};
+} status_t;
 
 /* ----------------- Parameter --------------------------------- */
 
diff --git a/src/magic.h b/src/magic.h
index cbd508163..98d2dabe8 100644
--- a/src/magic.h
+++ b/src/magic.h
@@ -20,6 +20,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #define H_KRNL_MAGIC
 
 #include <kernel/types.h>
+#include <util/variant.h>
 #include <stdbool.h>
 
 #ifdef __cplusplus
diff --git a/src/volcano.h b/src/volcano.h
index 225a76143..c72801cce 100644
--- a/src/volcano.h
+++ b/src/volcano.h
@@ -19,6 +19,9 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 #ifndef H_GC_VOLCANO
 #define H_GC_VOLCANO
+
+#include <stdbool.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif

From a9375200e4c02282e321300dabf210fe3f2f2086 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Thu, 15 Feb 2018 20:35:38 +0100
Subject: [PATCH 25/40] use_potion has a lot of scaffolding that
 use_healingpotion was duplicating.

---
 src/alchemy.c | 74 ++++++++++++++++++++++++++++++++-------------------
 src/battle.c  |  1 +
 src/items.c   | 36 ++-----------------------
 3 files changed, 50 insertions(+), 61 deletions(-)

diff --git a/src/alchemy.c b/src/alchemy.c
index b766278c6..bfa371c8e 100644
--- a/src/alchemy.c
+++ b/src/alchemy.c
@@ -154,8 +154,6 @@ static int begin_potion(unit * u, const item_type * itype, struct order *ord)
 
 static void end_potion(unit * u, const item_type * itype, int amount)
 {
-    use_pooled(u, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK,
-        amount);
     usetpotionuse(u, itype);
 
     ADDMSG(&u->faction->msgs, msg_message("usepotion",
@@ -173,12 +171,10 @@ static int potion_water_of_life(unit * u, region *r, int amount) {
     }
     /* mallorn is required to make mallorn forests, wood for regular ones */
     if (fval(r, RF_MALLORN)) {
-        wood = use_pooled(u, rt_find("mallorn"),
-            GET_SLACK | GET_RESERVE | GET_POOLED_SLACK, tree_count * amount);
+        wood = use_pooled(u, rt_find("mallorn"), GET_DEFAULT, tree_count * amount);
     }
     else {
-        wood = use_pooled(u, rt_find("log"),
-            GET_SLACK | GET_RESERVE | GET_POOLED_SLACK, tree_count * amount);
+        wood = use_pooled(u, rt_find("log"), GET_DEFAULT, tree_count * amount);
     }
     if (r->land == 0)
         wood = 0;
@@ -211,13 +207,6 @@ void show_potions(faction *f, int sklevel)
     }
 }
 
-static int potion_ointment(unit * u, int amount) {
-    int maxhp = unit_max_hp(u) * u->number;
-    u->hp = u->hp + 400 * amount;
-    if (u->hp > maxhp) u->hp = maxhp;
-    return amount;
-}
-
 static int potion_luck(unit *u, region *r, attrib_type *atype, int amount) {
     attrib *a = (attrib *)a_find(r->attribs, atype);
     UNUSED_ARG(u);
@@ -239,20 +228,56 @@ static int potion_power(unit *u, int amount) {
     return amount;
 }
 
+static int heal(unit * user, int effect)
+{
+    int req = unit_max_hp(user) * user->number - user->hp;
+    if (req > 0) {
+        if (req > effect) req = effect;
+        effect -= req;
+        user->hp += req;
+    }
+    return effect;
+}
+
+static int potion_ointment(unit * u, int amount) {
+    int effect = amount * 400;
+    effect = heal(u, effect);
+    return amount;
+}
+
+static int potion_healing(struct unit *user, int amount)
+{
+    int effect = amount * 400;
+    unit *u = user->region->units;
+    effect = heal(user, effect);
+    while (effect > 0 && u != NULL) {
+        if (u->faction == user->faction) {
+            effect = heal(u, effect);
+        }
+        u = u->next;
+    }
+    return amount;
+}
+
+
 static int do_potion(unit * u, region *r, const item_type * itype, int amount)
 {
+    /* TODO: why do some of these take a region argument? */
     if (itype == oldpotiontype[P_LIFE]) {
         return potion_water_of_life(u, r, amount);
     }
-    else if (itype == oldpotiontype[P_OINTMENT]) {
-        return potion_ointment(u, amount);
-    }
     else if (itype == oldpotiontype[P_PEOPLE]) {
         return potion_luck(u, r, &at_peasantluck, amount);
     }
     else if (itype == oldpotiontype[P_HORSE]) {
         return potion_luck(u, r, &at_horseluck, amount);
     }
+    else if (itype == oldpotiontype[P_HEAL]) {
+        return potion_healing(u, amount);
+    }
+    else if (itype == oldpotiontype[P_OINTMENT]) {
+        return potion_ointment(u, amount);
+    }
     else if (itype == oldpotiontype[P_MACHT]) {
         return potion_power(u, amount);
     }
@@ -264,17 +289,12 @@ static int do_potion(unit * u, region *r, const item_type * itype, int amount)
 
 int use_potion(unit * u, const item_type * itype, int amount, struct order *ord)
 {
-    if (oldpotiontype[P_HEAL] && itype == oldpotiontype[P_HEAL]) {
-        return EUNUSABLE;
-    }
-    else {
-        int result = begin_potion(u, itype, ord);
-        if (result)
-            return result;
-        amount = do_potion(u, u->region, itype, amount);
-        end_potion(u, itype, amount);
-    }
-    return 0;
+    int result = begin_potion(u, itype, ord);
+    if (result)
+        return result;
+    amount = do_potion(u, u->region, itype, amount);
+    end_potion(u, itype, amount);
+    return amount;
 }
 
 /*****************/
diff --git a/src/battle.c b/src/battle.c
index b7482e48a..58e8cf9fc 100644
--- a/src/battle.c
+++ b/src/battle.c
@@ -1300,6 +1300,7 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile)
         return false;
     }
 
+    /* healing potions can avert a killing blow */
     if (oldpotiontype[P_HEAL] && !fval(&df->person[dt.index], FL_HEALING_USED)) {
         if (i_get(du->items, oldpotiontype[P_HEAL]) > 0) {
             message *m = msg_message("potionsave", "unit", du);
diff --git a/src/items.c b/src/items.c
index f1940a644..d4ceca78a 100644
--- a/src/items.c
+++ b/src/items.c
@@ -310,39 +310,6 @@ struct order *ord)
     return 0;
 }
 
-static int heal(unit * user, int effect)
-{
-    int req = unit_max_hp(user) * user->number - user->hp;
-    if (req > 0) {
-        if (req > effect) req = effect;
-        effect -= req;
-        user->hp += req;
-    }
-    return effect;
-}
-
-static int
-use_healingpotion(struct unit *user, const struct item_type *itype,
-    int amount, struct order *ord)
-{
-    int effect = amount * 400;
-    unit *u = user->region->units;
-    effect = heal(user, effect);
-    while (effect > 0 && u != NULL) {
-        if (u->faction == user->faction) {
-            effect = heal(u, effect);
-        }
-        u = u->next;
-    }
-    use_pooled(user, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK,
-        amount);
-    usetpotionuse(user, itype);
-
-    ADDMSG(&user->faction->msgs, msg_message("usepotion",
-        "unit potion", user, itype->rtype));
-    return 0;
-}
-
 /* ------------------------------------------------------------- */
 /* Kann auch von Nichtmagier benutzt werden, modifiziert Taktik fuer diese
 * Runde um -1 - 4 Punkte. */
@@ -412,6 +379,7 @@ static int use_warmthpotion(unit *u, const item_type *itype,
 void register_itemfunctions(void)
 {
     /* have tests: */
+    /* TODO: potions should really use use_potion */
     register_item_use(use_mistletoe, "use_mistletoe");
     register_item_use(use_tacticcrystal, "use_dreameye");
     register_item_use(use_studypotion, "use_studypotion");
@@ -423,7 +391,7 @@ void register_itemfunctions(void)
     register_item_use(use_foolpotion, "use_p7");
     register_item_use(use_bloodpotion, "use_peasantblood");
     register_item_use(use_potion, "use_ointment");
-    register_item_use(use_healingpotion, "use_p14");
+    register_item_use(use_potion, "use_p14");
     register_item_use(use_warmthpotion, "use_nestwarmth");
     /* p2 = P_LIFE = Wasser des Lebens */
     register_item_use(use_potion, "use_p2");

From ad86e69e6b11b94cdff7cbcfc2ff7bf706de8710 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Thu, 15 Feb 2018 21:05:11 +0100
Subject: [PATCH 26/40] refactor the resurrection code. There is more work to
 be done here, too much duplicaton between battle and volcano.

---
 src/battle.c  | 30 ++++++++++++++++++++----------
 src/items.h   |  7 ++++++-
 src/volcano.c | 49 +++++++++++++++++++++++++++----------------------
 3 files changed, 53 insertions(+), 33 deletions(-)

diff --git a/src/battle.c b/src/battle.c
index 58e8cf9fc..e82ba71a5 100644
--- a/src/battle.c
+++ b/src/battle.c
@@ -1130,6 +1130,21 @@ int calculate_armor(troop dt, const weapon_type *dwtype, const weapon_type *awty
     return ar;
 }
 
+static bool resurrect_troop(troop dt)
+{
+    fighter *df = dt.fighter;
+    unit *du = df->unit;
+    if (oldpotiontype[P_HEAL] && !fval(&df->person[dt.index], FL_HEALING_USED)) {
+        if (i_get(du->items, oldpotiontype[P_HEAL]) > 0) {
+            fset(&df->person[dt.index], FL_HEALING_USED);
+            i_change(&du->items, oldpotiontype[P_HEAL], -1);
+            df->person[dt.index].hp = u_race(du)->hitpoints * 5; /* give the person a buffer */
+            return true;
+        }
+    }
+    return false;
+}
+
 bool
 terminate(troop dt, troop at, int type, const char *damage, bool missile)
 {
@@ -1301,16 +1316,11 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile)
     }
 
     /* healing potions can avert a killing blow */
-    if (oldpotiontype[P_HEAL] && !fval(&df->person[dt.index], FL_HEALING_USED)) {
-        if (i_get(du->items, oldpotiontype[P_HEAL]) > 0) {
-            message *m = msg_message("potionsave", "unit", du);
-            battle_message_faction(b, du->faction, m);
-            msg_release(m);
-            i_change(&du->items, oldpotiontype[P_HEAL], -1);
-            fset(&df->person[dt.index], FL_HEALING_USED);
-            df->person[dt.index].hp = u_race(du)->hitpoints * 5; /* give the person a buffer */
-            return false;
-        }
+    if (resurrect_troop(dt)) {
+        message *m = msg_message("potionsave", "unit", du);
+        battle_message_faction(b, du->faction, m);
+        msg_release(m);
+        return false;
     }
     ++at.fighter->kills;
 
diff --git a/src/items.h b/src/items.h
index 2b839563d..f37444b74 100644
--- a/src/items.h
+++ b/src/items.h
@@ -12,11 +12,16 @@ without prior permission by the authors of Eressea.
 
 #ifndef H_KRNL_ITEMS
 #define H_KRNL_ITEMS
+
+#include <stdbool.h>
+
+struct unit;
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-    extern void register_itemfunctions(void);
+    void register_itemfunctions(void);
 
 #ifdef __cplusplus
 }
diff --git a/src/volcano.c b/src/volcano.c
index df70a864f..a1b2240e2 100644
--- a/src/volcano.c
+++ b/src/volcano.c
@@ -74,12 +74,31 @@ static int nb_armor(const unit * u, int index)
     return av;
 }
 
+static bool resurrect_unit(unit *u) {
+    if (oldpotiontype[P_HEAL]) {
+        bool heiltrank = false;
+        if (get_effect(u, oldpotiontype[P_HEAL]) > 0) {
+            change_effect(u, oldpotiontype[P_HEAL], -1);
+            heiltrank = true;
+        }
+        else if (i_get(u->items, oldpotiontype[P_HEAL]) > 0) {
+            i_change(&u->items, oldpotiontype[P_HEAL], -1);
+            change_effect(u, oldpotiontype[P_HEAL], 3);
+            heiltrank = true;
+        }
+        if (heiltrank && chance(0.50)) {
+            return true;
+        }
+    }
+    return false;
+}
+
 static int
 damage_unit(unit * u, const char *dam, bool physical, bool magic)
 {
     int *hp, hpstack[20];
     int h;
-    int i, dead = 0, hp_rem = 0, heiltrank;
+    int i, dead = 0, hp_rem = 0;
 
     assert(u->number);
     if (fval(u_race(u), RCF_ILLUSIONARY)) {
@@ -118,33 +137,19 @@ damage_unit(unit * u, const char *dam, bool physical, bool magic)
     /* Auswirkungen */
     for (i = 0; i < u->number; i++) {
         if (hp[i] <= 0) {
-            heiltrank = 0;
-
             /* Sieben Leben */
             if (u_race(u) == get_race(RC_CAT) && (chance(1.0 / 7))) {
                 hp[i] = u->hp / u->number;
                 hp_rem += hp[i];
-                continue;
             }
-
-            /* Heiltrank */
-            if (oldpotiontype[P_HEAL]) {
-                if (get_effect(u, oldpotiontype[P_HEAL]) > 0) {
-                    change_effect(u, oldpotiontype[P_HEAL], -1);
-                    heiltrank = 1;
-                }
-                else if (i_get(u->items, oldpotiontype[P_HEAL]) > 0) {
-                    i_change(&u->items, oldpotiontype[P_HEAL], -1);
-                    change_effect(u, oldpotiontype[P_HEAL], 3);
-                    heiltrank = 1;
-                }
-                if (heiltrank && (chance(0.50))) {
-                    hp[i] = u->hp / u->number;
-                    hp_rem += hp[i];
-                    continue;
-                }
+            else if (resurrect_unit(u)) {
+                /* Heiltrank */
+                hp[i] = u->hp / u->number;
+                hp_rem += hp[i];
+            }
+            else {
+                ++dead;
             }
-            dead++;
         }
         else {
             hp_rem += hp[i];

From e697a23f9d91fb08eac36ee4e582845173225220 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Fri, 16 Feb 2018 20:06:36 +0100
Subject: [PATCH 27/40] BUG 2419: fix and speed up volcano-survival through
 potions.

---
 src/volcano.c | 31 ++++++++++++++++++++-----------
 1 file changed, 20 insertions(+), 11 deletions(-)

diff --git a/src/volcano.c b/src/volcano.c
index a1b2240e2..7bddd6097 100644
--- a/src/volcano.c
+++ b/src/volcano.c
@@ -86,18 +86,18 @@ static bool resurrect_unit(unit *u) {
             change_effect(u, oldpotiontype[P_HEAL], 3);
             heiltrank = true;
         }
-        if (heiltrank && chance(0.50)) {
+        if (heiltrank && (rng_int() % 2)) {
             return true;
         }
     }
     return false;
 }
 
-static int
-damage_unit(unit * u, const char *dam, bool physical, bool magic)
+static int damage_unit(unit * u, const char *dam, bool physical, bool magic)
 {
     int *hp, hpstack[20];
     int h;
+    int healings;
     int i, dead = 0, hp_rem = 0;
 
     assert(u->number);
@@ -134,6 +134,9 @@ damage_unit(unit * u, const char *dam, bool physical, bool magic)
         hp[i] -= damage;
     }
 
+    /* does this unit have any healing potions or effects? */
+    healings = i_get(u->items, oldpotiontype[P_HEAL]) * 4;
+    healings += get_effect(u, oldpotiontype[P_HEAL]);
     /* Auswirkungen */
     for (i = 0; i < u->number; i++) {
         if (hp[i] <= 0) {
@@ -142,10 +145,16 @@ damage_unit(unit * u, const char *dam, bool physical, bool magic)
                 hp[i] = u->hp / u->number;
                 hp_rem += hp[i];
             }
-            else if (resurrect_unit(u)) {
-                /* Heiltrank */
-                hp[i] = u->hp / u->number;
-                hp_rem += hp[i];
+            else if (healings > 0) {
+                --healings;
+                if (resurrect_unit(u)) {
+                    /* Heiltrank benutzen */
+                    hp[i] = u->hp / u->number;
+                    hp_rem += hp[i];
+                }
+                else {
+                    ++dead;
+                }
             }
             else {
                 ++dead;
@@ -268,7 +277,7 @@ static bool stop_smoke_chance(void) {
     if (config_changed(&cache)) {
         percent = config_get_int("volcano.stop.percent", 12);
     }
-    return percent!=0 && (rng_int() % 100) < percent;
+    return percent != 0 && (rng_int() % 100) < percent;
 }
 
 static bool outbreak_chance(void) {
@@ -276,12 +285,12 @@ static bool outbreak_chance(void) {
     if (config_changed(&cache)) {
         percent = config_get_int("volcano.outbreak.percent", 8);
     }
-    return percent!=0 && (rng_int() % 100) < percent;
+    return percent != 0 && (rng_int() % 100) < percent;
 }
 
-void volcano_update(void) 
+void volcano_update(void)
 {
-    region *r; 
+    region *r;
     const struct terrain_type *t_active, *t_volcano;
 
     t_volcano = get_terrain("volcano");

From 8a8bf489ae11e7c42d577df054ece272d6cb3f38 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sat, 17 Feb 2018 10:39:23 +0100
Subject: [PATCH 28/40] clarify use_potion responsibilities. move USE potion of
 life to items.c. fix some use_ function return values.

---
 scripts/eressea/xmasitems.lua |  8 +++--
 src/alchemy.c                 | 38 ++------------------
 src/helpers.c                 | 67 ++++++++++++++++++++++-------------
 src/items.c                   | 39 +++++++++++++++++++-
 4 files changed, 88 insertions(+), 64 deletions(-)

diff --git a/scripts/eressea/xmasitems.lua b/scripts/eressea/xmasitems.lua
index dffea2675..9e22dea52 100644
--- a/scripts/eressea/xmasitems.lua
+++ b/scripts/eressea/xmasitems.lua
@@ -62,10 +62,11 @@ function use_snowglobe(u, amount, token, ord)
 end
 
 function use_snowman(u, amount)
-    if amount>0 and u.region.terrain == "glacier" then
-        local man = unit.create(u.faction, u.region, amount, "snowman")
+    if amount > 0 and u.region.terrain == "glacier" then
+        unit.create(u.faction, u.region, amount, "snowman")
         return amount
     end
+    -- print error76:
     return -4
 end
 
@@ -79,7 +80,8 @@ function use_xmastree(u, amount)
         msg:send_region(u.region)
         return amount
     end
-    return 0
+    -- print error76:
+    return -4
 end
 
 local self = {}
diff --git a/src/alchemy.c b/src/alchemy.c
index bfa371c8e..c6c550774 100644
--- a/src/alchemy.c
+++ b/src/alchemy.c
@@ -160,37 +160,6 @@ static void end_potion(unit * u, const item_type * itype, int amount)
         "unit potion", u, itype->rtype));
 }
 
-static int potion_water_of_life(unit * u, region *r, int amount) {
-    static int config;
-    static int tree_type, tree_count;
-    int wood = 0;
-
-    if (config_changed(&config)) {
-        tree_type = config_get_int("rules.magic.wol_type", 1);
-        tree_count = config_get_int("rules.magic.wol_effect", 10);
-    }
-    /* mallorn is required to make mallorn forests, wood for regular ones */
-    if (fval(r, RF_MALLORN)) {
-        wood = use_pooled(u, rt_find("mallorn"), GET_DEFAULT, tree_count * amount);
-    }
-    else {
-        wood = use_pooled(u, rt_find("log"), GET_DEFAULT, tree_count * amount);
-    }
-    if (r->land == 0)
-        wood = 0;
-    if (wood < tree_count * amount) {
-        int x = wood / tree_count;
-        if (wood % tree_count)
-            ++x;
-        if (x < amount)
-            amount = x;
-    }
-    rsettrees(r, tree_type, rtrees(r, tree_type) + wood);
-    ADDMSG(&u->faction->msgs, msg_message("growtree_effect",
-        "mage amount", u, wood));
-    return amount;
-}
-
 void show_potions(faction *f, int sklevel)
 {
     const potion_type *ptype;
@@ -262,11 +231,8 @@ static int potion_healing(struct unit *user, int amount)
 
 static int do_potion(unit * u, region *r, const item_type * itype, int amount)
 {
-    /* TODO: why do some of these take a region argument? */
-    if (itype == oldpotiontype[P_LIFE]) {
-        return potion_water_of_life(u, r, amount);
-    }
-    else if (itype == oldpotiontype[P_PEOPLE]) {
+    /* TODO: this function should only be used for effect-changing potions */
+    if (itype == oldpotiontype[P_PEOPLE]) {
         return potion_luck(u, r, &at_peasantluck, amount);
     }
     else if (itype == oldpotiontype[P_HORSE]) {
diff --git a/src/helpers.c b/src/helpers.c
index e6015488a..d3ff9870f 100644
--- a/src/helpers.c
+++ b/src/helpers.c
@@ -1,4 +1,4 @@
-/* 
+/*
 +-------------------+
 |                   |  Enno Rehling <enno@eressea.de>
 | Eressea PBEM host |  Christian Schlittchen <corwin@amber.kn-bremen.de>
@@ -228,7 +228,7 @@ lua_changeresource(unit * u, const struct resource_type *rtype, int delta)
         if (lua_isfunction(L, -1)) {
             tolua_pushusertype(L, u, TOLUA_CAST "unit");
             lua_pushinteger(L, delta);
-            
+
             if (lua_pcall(L, 2, 1, 0) != 0) {
                 const char *error = lua_tostring(L, -1);
                 log_error("change(%s) calling '%s': %s.\n", unitname(u), fname, error);
@@ -249,19 +249,9 @@ lua_changeresource(unit * u, const struct resource_type *rtype, int delta)
 
 /** callback for an item-use function written in lua. */
 static int
-use_item_lua(unit *u, const item_type *itype, int amount, struct order *ord)
+lua_use_item(unit *u, const item_type *itype, const char * fname, int amount, struct order *ord)
 {
     lua_State *L = (lua_State *)global.vm_state;
-    int len, result = 0;
-    char fname[64];
-    int (*callout)(unit *, const item_type *, int, struct order *);
-
-    len = snprintf(fname, sizeof(fname), "use_%s", itype->rtype->_name);
-    if (len > 0 && (size_t)len < sizeof(fname)) {
-    callout = (int(*)(unit *, const item_type *, int, struct order *))get_function(fname);
-    if (callout) {
-        return callout(u, itype, amount, ord);
-    }
 
     lua_getglobal(L, fname);
     if (lua_isfunction(L, -1)) {
@@ -272,23 +262,52 @@ use_item_lua(unit *u, const item_type *itype, int amount, struct order *ord)
         if (lua_pcall(L, 4, 1, 0) != 0) {
             const char *error = lua_tostring(L, -1);
             log_error("use(%s) calling '%s': %s.\n", unitname(u), fname, error);
-            lua_pop(L, 1);
         }
         else {
-            result = (int)lua_tonumber(L, -1);
+            int result = (int)lua_tonumber(L, -1);
             lua_pop(L, 1);
+            return result;
         }
-        return result;
     }
     lua_pop(L, 1);
-    if (itype->flags & ITF_POTION) {
-        return use_potion(u, itype, amount, ord);
-    } else {
-        log_error("no such callout: %s", fname);
+    return 0;
+}
+
+static int
+use_item_callback(unit *u, const item_type *itype, int amount, struct order *ord)
+{
+    int len;
+    char fname[64];
+    int(*callout)(unit *, const item_type *, int, struct order *);
+
+    len = snprintf(fname, sizeof(fname), "use_%s", itype->rtype->_name);
+    if (len > 0 && (size_t)len < sizeof(fname)) {
+        int result;
+
+        /* check if we have a register_item_use function */
+        callout = (int(*)(unit *, const item_type *, int, struct order *))get_function(fname);
+        if (callout) {
+            return callout(u, itype, amount, ord);
+        }
+
+        /* check if we have a matching lua function */
+        result = lua_use_item(u, itype, fname, amount, ord);
+        if (result != 0) {
+            return result;
+        }
+
+        /* if the item is a potion, try use_potion, the generic function for 
+         * potions that add an effect: */
+        if (itype->flags & ITF_POTION) {
+            return use_potion(u, itype, amount, ord);
+        }
+        else {
+            log_error("no such callout: %s", fname);
+        }
+        log_error("use(%s) calling '%s': not a function.\n", unitname(u), fname);
     }
-    log_error("use(%s) calling '%s': not a function.\n", unitname(u), fname);
-    }
-    return result;
+
+    return 0;
 }
 
 /* compat code for old data files */
@@ -328,7 +347,7 @@ void register_tolua_helpers(void)
     at_deprecate("lcbuilding", building_action_read);
 
     callbacks.cast_spell = lua_callspell;
-    callbacks.use_item = use_item_lua;
+    callbacks.use_item = use_item_callback;
     callbacks.produce_resource = produce_resource_lua;
     callbacks.limit_resource = limit_resource_lua;
 
diff --git a/src/items.c b/src/items.c
index d4ceca78a..5986675c6 100644
--- a/src/items.c
+++ b/src/items.c
@@ -376,10 +376,47 @@ static int use_warmthpotion(unit *u, const item_type *itype,
     return 0;
 }
 
+static int potion_water_of_life(unit * u, region *r, int amount) {
+    static int config;
+    static int tree_type, tree_count;
+    int wood = 0;
+
+    if (config_changed(&config)) {
+        tree_type = config_get_int("rules.magic.wol_type", 1);
+        tree_count = config_get_int("rules.magic.wol_effect", 10);
+    }
+    /* mallorn is required to make mallorn forests, wood for regular ones */
+    if (fval(r, RF_MALLORN)) {
+        wood = use_pooled(u, rt_find("mallorn"), GET_DEFAULT, tree_count * amount);
+    }
+    else {
+        wood = use_pooled(u, rt_find("log"), GET_DEFAULT, tree_count * amount);
+    }
+    if (r->land == 0)
+        wood = 0;
+    if (wood < tree_count * amount) {
+        int x = wood / tree_count;
+        if (wood % tree_count)
+            ++x;
+        if (x < amount)
+            amount = x;
+    }
+    rsettrees(r, tree_type, rtrees(r, tree_type) + wood);
+    ADDMSG(&u->faction->msgs, msg_message("growtree_effect",
+        "mage amount", u, wood));
+    return amount;
+}
+
+static int use_water_of_life(unit *u, const item_type *itype,
+    int amount, struct order *ord)
+{
+    return potion_water_of_life(u, u->region, amount);
+}
+
 void register_itemfunctions(void)
 {
     /* have tests: */
-    /* TODO: potions should really use use_potion */
+    register_item_use(use_water_of_life, "use_p2");
     register_item_use(use_mistletoe, "use_mistletoe");
     register_item_use(use_tacticcrystal, "use_dreameye");
     register_item_use(use_studypotion, "use_studypotion");

From 96062f6f85e72805d1381a780d75791f6e24c608 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sat, 17 Feb 2018 10:45:56 +0100
Subject: [PATCH 29/40] rename p2->lifepotion (WdL)

---
 conf/e2/rules.xml           |  2 +-
 res/core/common/potions.xml |  2 +-
 res/core/de/strings.xml     |  4 ++--
 res/core/en/strings.xml     |  2 +-
 res/core/fr/strings.xml     |  2 +-
 res/core/spells.xml         |  2 +-
 res/e3a/items.xml           |  2 +-
 res/e3a/strings.xml         |  2 +-
 res/eressea/spells.xml      | 14 +++++++-------
 scripts/eressea/spells.lua  |  2 +-
 scripts/tests/e3/rules.lua  |  6 +++---
 src/items.c                 |  4 +---
 src/kernel/item.c           |  5 +++--
 13 files changed, 24 insertions(+), 25 deletions(-)

diff --git a/conf/e2/rules.xml b/conf/e2/rules.xml
index 9e40910e0..002c34960 100644
--- a/conf/e2/rules.xml
+++ b/conf/e2/rules.xml
@@ -15,7 +15,7 @@
       <item name="sword" amount="1"/>
       <item name="mallorn" amount="10"/>
       <item name="skillpotion" amount="5"/>
-      <item name="p2" amount="5"/>
+      <item name="lifepotion" amount="5"/>
       <item name="money" amount="20000"/>
       <skill name="perception" level="30"/>
       <skill name="melee" level="1"/>
diff --git a/res/core/common/potions.xml b/res/core/common/potions.xml
index bb4556eb7..ab2b8c951 100644
--- a/res/core/common/potions.xml
+++ b/res/core/common/potions.xml
@@ -34,7 +34,7 @@
     </item>
   </resource>
 
-  <resource name="p2" appearance="vial">
+  <resource name="lifepotion" appearance="vial">
     <item weight="0" score="30" use="yes">
       <potion level="1"/>
       <construction skill="alchemy" minskill="2">
diff --git a/res/core/de/strings.xml b/res/core/de/strings.xml
index 896a7e009..28e524b38 100644
--- a/res/core/de/strings.xml
+++ b/res/core/de/strings.xml
@@ -1863,7 +1863,7 @@
   <string name="goliathwater">
     <text locale="de">Goliathwasser</text>
   </string>
-  <string name="p2">
+  <string name="lifepotion">
     <text locale="de">Wasser des Lebens</text>
   </string>
   <string name="p3">
@@ -3592,7 +3592,7 @@
       <text locale="en">'First roast the Gurgelkraut quickly and add some Fjordwuchs to spice it up. Let it all boil slowly until almost all liquid has evaporated. Leave the mash overnight and finally squeeze it the next morning until a thick fluid drips out.' The liquid thus produced, 'Goliath Water' as we call it, is enough for 10 men and gives each man the carrying capacity of a horse for one week.</text>
       <text locale="de">Zuerst brate man das Gurgelkraut leicht an und würze das Zeug mit ein wenig Fjordwuchs. Man lasse alles so lange kochen, bis fast alle Flüssigkeit verdampft ist. Diesen Brei stelle man über Nacht raus. Am nächsten Morgen presse man den Brei aus. Die so gewonnene Flüssigkeit, Goliathwasser genannt, verleiht bis zu zehn Männern die Tragkraft eines Pferdes.</text>
     </string>
-    <string name="p2">
+    <string name="lifepotion">
       <text locale="en">The "Water of Life" allows living trees to be created from logs. A Knotroot and Elvendear are heated until one can just still keep one's finger in. This is then poured into a jar and allowed to cool slowly. The extract is sufficient for 10 pieces of wood.</text>
       <text locale="de">Das 'Wasser des Lebens' ist in der Lage, aus gefällten Baumstämmen wieder lebende Bäume zu machen. Dazu wird ein knotiger Saugwurz zusammen mit einem Elfenlieb erwärmt, so dass man gerade noch den Finger reinhalten kann. Dies gieße man in ein Gefäß und lasse es langsam abkühlen. Der Extrakt reicht für 10 Holzstämme.</text>
     </string>
diff --git a/res/core/en/strings.xml b/res/core/en/strings.xml
index f818a3931..5324b165d 100644
--- a/res/core/en/strings.xml
+++ b/res/core/en/strings.xml
@@ -1078,7 +1078,7 @@
   <string name="goliathwater_p">
     <text locale="en">goliath waters</text>
   </string>
-  <string name="p2">
+  <string name="lifepotion">
     <text locale="en">water of life</text>
   </string>
   <string name="p2_p">
diff --git a/res/core/fr/strings.xml b/res/core/fr/strings.xml
index 13e6beb25..f81b5fc96 100644
--- a/res/core/fr/strings.xml
+++ b/res/core/fr/strings.xml
@@ -1085,7 +1085,7 @@
   <string name="p1_p">
     <text locale="fr">breuvage de Goliath</text>
   </string>
-  <string name="p2">
+  <string name="lifepotion">
     <text locale="fr">élixir de vie</text>
   </string>
   <string name="p2_p">
diff --git a/res/core/spells.xml b/res/core/spells.xml
index c4df3e429..fba087855 100644
--- a/res/core/spells.xml
+++ b/res/core/spells.xml
@@ -39,7 +39,7 @@
   <spell name="create_magicherbbag" ship="true" rank="5" index="165">
     <resource name="aura" amount="30" cost="fixed"/>
     <resource name="permaura" amount="1" cost="fixed"/>
-    <resource name="p2" amount="1" cost="fixed"/>
+    <resource name="lifepotion" amount="1" cost="fixed"/>
   </spell>
 
   <!-- illaun spells -->
diff --git a/res/e3a/items.xml b/res/e3a/items.xml
index 1d403cfb4..8f84796be 100644
--- a/res/e3a/items.xml
+++ b/res/e3a/items.xml
@@ -66,7 +66,7 @@
     </item>
   </resource>
 
-  <resource name="p2" appearance="vial">
+  <resource name="lifepotion" appearance="vial">
     <!-- Wasser des Lebens -->
     <item weight="0" score="30" use="yes">
       <potion level="1"/>
diff --git a/res/e3a/strings.xml b/res/e3a/strings.xml
index 2dd79abb6..f68720e6a 100644
--- a/res/e3a/strings.xml
+++ b/res/e3a/strings.xml
@@ -284,7 +284,7 @@
   </namespace>
 
   <namespace name="potion">
-    <string name="p2">
+    <string name="lifepotion">
       <text locale="en">The "Water of Life" allows living trees to be created from logs. A Knotroot and Elvendear are heated until one can just still keep one's finger in. This is then poured into a jar and allowed to cool slowly. The extract is sufficient for five trees to be grown from logs.</text>
       <text locale="de">Das 'Wasser des Lebens' ist in der Lage, aus gefällten Baumstämmen wieder lebende Bäume zu machen. Dazu wird ein knotiger Saugwurz zusammen mit einem Elfenlieb erwärmt, so dass man gerade noch den Finger reinhalten kann. Dies gieße man in ein Gefäß und lasse es langsam abkühlen. Der Extrakt reicht um aus fünf Holzstämmen neue Bäume wachsen zu lassen.</text>
     </string>
diff --git a/res/eressea/spells.xml b/res/eressea/spells.xml
index 089ec1a6c..7680f2f3b 100644
--- a/res/eressea/spells.xml
+++ b/res/eressea/spells.xml
@@ -32,7 +32,7 @@
   <spell name="treegrow" rank="5" index="8" far="true" variable="true">
     <resource name="aura" amount="4" cost="level"/>
     <resource name="log" amount="1" cost="level"/>
-    <resource name="p2" amount="1" cost="fixed"/>
+    <resource name="lifepotion" amount="1" cost="fixed"/>
   </spell>
   <spell name="healing" rank="5" index="9" variable="true" combat="3">
     <resource name="aura" amount="1" cost="level"/>
@@ -101,12 +101,12 @@
   <spell name="stonegolem" rank="4" index="32" variable="true">
     <resource name="aura" amount="2" cost="level"/>
     <resource name="stone" amount="1" cost="level"/>
-    <resource name="p2" amount="1" cost="fixed"/>
+    <resource name="lifepotion" amount="1" cost="fixed"/>
   </spell>
   <spell name="irongolem" rank="4" index="33" variable="true">
     <resource name="aura" amount="2" cost="level"/>
     <resource name="iron" amount="1" cost="level"/>
-    <resource name="p2" amount="1" cost="fixed"/>
+    <resource name="lifepotion" amount="1" cost="fixed"/>
   </spell>
   <spell name="summonshadow" rank="5" index="34" variable="true">
     <resource name="aura" amount="3" cost="level"/>
@@ -433,7 +433,7 @@
   </spell>
   <spell name="puttorest" rank="5" index="168" variable="true">
     <resource name="aura" amount="3" cost="level"/>
-    <resource name="p2" amount="1" cost="fixed"/>
+    <resource name="lifepotion" amount="1" cost="fixed"/>
   </spell>
   <spell name="unholypower" rank="5" index="169" parameters="u+" los="true" variable="true">
     <resource name="aura" amount="10" cost="level"/>
@@ -455,7 +455,7 @@
     <resource name="aura" amount="100" cost="fixed"/>
     <resource name="permaura" amount="20" cost="fixed"/>
     <resource name="dragonblood" amount="5" cost="fixed"/>
-    <resource name="p2" amount="5" cost="fixed"/>
+    <resource name="lifepotion" amount="5" cost="fixed"/>
   </spell>
   <spell name="drain_skills" rank="5" index="174" combat="2">
     <resource name="aura" amount="4" cost="fixed"/>
@@ -466,7 +466,7 @@
   <spell name="mallorntreegrow" rank="5" index="177" far="true" variable="true">
     <resource name="aura" amount="6" cost="level"/>
     <resource name="mallorn" amount="1" cost="level"/>
-    <resource name="p2" amount="1" cost="fixed"/>
+    <resource name="lifepotion" amount="1" cost="fixed"/>
   </spell>
   <spell name="big_recruit" rank="5" index="179" variable="true">
     <resource name="aura" amount="20" cost="level"/>
@@ -515,7 +515,7 @@
   <spell name="create_magicherbbag" ship="true" rank="5" index="165">
     <resource name="aura" amount="30" cost="fixed"/>
     <resource name="permaura" amount="1" cost="fixed"/>
-    <resource name="p2" amount="1" cost="fixed"/>
+    <resource name="lifepotion" amount="1" cost="fixed"/>
   </spell>
 
   <!-- illaun spells -->
diff --git a/scripts/eressea/spells.lua b/scripts/eressea/spells.lua
index 5e04c4ed1..5f1507d41 100644
--- a/scripts/eressea/spells.lua
+++ b/scripts/eressea/spells.lua
@@ -26,7 +26,7 @@ end
 
 -- Wasser des Lebens
 function create_potion_p2(r, mage, level, force)
-  return create_potion(mage, level, "p2", force)
+  return create_potion(mage, level, "lifepotion", force)
 end
 
 -- Siebenmeilentee
diff --git a/scripts/tests/e3/rules.lua b/scripts/tests/e3/rules.lua
index 0888aa0b9..5120fb747 100644
--- a/scripts/tests/e3/rules.lua
+++ b/scripts/tests/e3/rules.lua
@@ -783,12 +783,12 @@ function test_p2()
     r:set_resource("tree", 0)
     u:clear_orders()
     u:add_order("BENUTZE 'Wasser des Lebens'")
-    u:add_item("p2", 1)
+    u:add_item("lifepotion", 1)
     u:add_item("log", 10)
     u:add_item("mallorn", 10)
     process_orders()
     assert_equal(5, r:get_resource("tree"))
-    assert_equal(0, u:get_item("p2"))
+    assert_equal(0, u:get_item("lifepotion"))
     assert_equal(15, u:get_item("log") + u:get_item("mallorn"))
 end
 
@@ -803,7 +803,7 @@ function test_p2_move()
     u:add_order("BENUTZE 'Wasser des Lebens'")
     u:add_order("NACH OST")
     u:add_item("horse", 1)
-    u:add_item("p2", 1)
+    u:add_item("lifepotion", 1)
     u:add_item("log", 1)
     u:add_item("mallorn", 1)
     process_orders()
diff --git a/src/items.c b/src/items.c
index 5986675c6..5132b31cb 100644
--- a/src/items.c
+++ b/src/items.c
@@ -416,7 +416,7 @@ static int use_water_of_life(unit *u, const item_type *itype,
 void register_itemfunctions(void)
 {
     /* have tests: */
-    register_item_use(use_water_of_life, "use_p2");
+    register_item_use(use_water_of_life, "use_lifepotion");
     register_item_use(use_mistletoe, "use_mistletoe");
     register_item_use(use_tacticcrystal, "use_dreameye");
     register_item_use(use_studypotion, "use_studypotion");
@@ -430,6 +430,4 @@ void register_itemfunctions(void)
     register_item_use(use_potion, "use_ointment");
     register_item_use(use_potion, "use_p14");
     register_item_use(use_warmthpotion, "use_nestwarmth");
-    /* p2 = P_LIFE = Wasser des Lebens */
-    register_item_use(use_potion, "use_p2");
 }
diff --git a/src/kernel/item.c b/src/kernel/item.c
index 74951895a..db551bed3 100644
--- a/src/kernel/item.c
+++ b/src/kernel/item.c
@@ -208,6 +208,7 @@ static const char *it_aliases[][2] = {
     { "Runenschwert", "runesword" },
     { "p12", "truthpotion" },
     { "p1", "goliathwater" },
+    { "p2", "lifepotion" },
     { "p4", "ointment" },
     { "p5", "peasantblood" },
     { "p8", "nestwarmth" },
@@ -555,7 +556,7 @@ static const char *resourcenames[MAX_RESOURCES] = {
     "laen", "fairyboot", "aoc", "pegasus",
     "elvenhorse", "charger", "dolphin", "roqf", "trollbelt",
     "aurafocus", "sphereofinv", "magicbag",
-    "magicherbbag", "dreameye", "p2"
+    "magicherbbag", "dreameye", "lifepotion"
 };
 
 const resource_type *get_resourcetype(resource_t type) {
@@ -636,7 +637,7 @@ struct order *), const char *name)
 static void init_oldpotions(void)
 {
     const char *potionnames[MAX_POTIONS] = {
-        "p0", "goliathwater", "p2", "p3", "ointment", "peasantblood", "p6",
+        "p0", "goliathwater", "lifepotion", "p3", "ointment", "peasantblood", "p6",
         "p7", "nestwarmth", "p9", "p10", "p11", "truthpotion", "p13", "p14"
     };
     int p;

From a2d23d01faaf83fc3bd71bd1706cad0a78ad72dc Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sat, 17 Feb 2018 12:52:58 +0100
Subject: [PATCH 30/40] Test: Heiltrank heilt mehrere Einheiten.

---
 scripts/tests/e2/items.lua | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/scripts/tests/e2/items.lua b/scripts/tests/e2/items.lua
index fe3bca370..c66d08bfa 100644
--- a/scripts/tests/e2/items.lua
+++ b/scripts/tests/e2/items.lua
@@ -152,6 +152,27 @@ function test_use_healing_potion()
     turn_end()
 end
 
+function test_use_healing_potion_multi_units()
+    -- Heiltrank kann mehrere Einheiten heilen
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("human")
+    local u1 = unit.create(f, r, 30)
+    local u = unit.create(f, r, 30)
+    assert_equal(600, u1.hp)
+    assert_equal(600, u.hp)
+    u.hp = 400
+    u1.hp = 400
+    turn_begin()
+    u:add_item("p14", 1)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Heiltrank")
+    turn_process()
+    assert_equal(600, u.hp)
+    assert_equal(600, u1.hp)
+    assert_equal(0, u:get_item("p14"))
+    turn_end()
+end
+
 function test_speedsail()
     local r = region.create(0, 0, "plain")
     local f = faction.create("human")

From 438ae6f905b1a63246c78e6edc8c5bd9f68ba1ba Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sat, 17 Feb 2018 15:17:05 +0100
Subject: [PATCH 31/40] Eliminate rules.magic.multipotion, it's too much code.

---
 conf/e3/config.json        |  1 -
 scripts/tests/e2/items.lua | 35 +++++++++++++++++++++++++++++
 src/alchemy.c              | 45 +++-----------------------------------
 src/items.c                |  2 --
 src/kernel/unit.c          | 29 ------------------------
 src/kernel/unit.h          |  3 ---
 6 files changed, 38 insertions(+), 77 deletions(-)

diff --git a/conf/e3/config.json b/conf/e3/config.json
index 528d0a7f2..b5f9aed5c 100644
--- a/conf/e3/config.json
+++ b/conf/e3/config.json
@@ -93,7 +93,6 @@
         "rules.region_owners": true,
         "rules.cavalry.skill": 2,
         "rules.cavalry.mode": 1,
-        "rules.magic.multipotion": true,
         "rules.magic.wol_effect": 5,
         "rules.magic.factionlist": true,
         "rules.magic.wol_type": 2,
diff --git a/scripts/tests/e2/items.lua b/scripts/tests/e2/items.lua
index c66d08bfa..85ec40e19 100644
--- a/scripts/tests/e2/items.lua
+++ b/scripts/tests/e2/items.lua
@@ -173,6 +173,41 @@ function test_use_healing_potion_multi_units()
     turn_end()
 end
 
+function test_use_multiple_healing_potions()
+    -- Einheit kann mehr als einen Heiltrank benutzen
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("human")
+    local u = unit.create(f, r, 60)
+    assert_equal(1200, u.hp)
+    u.hp = 400
+    turn_begin()
+    u:add_item("p14", 2)
+    u:clear_orders()
+    u:add_order("BENUTZEN 2 Heiltrank")
+    turn_process()
+    assert_equal(1200, u.hp)
+    assert_equal(0, u:get_item("p14"))
+    turn_end()
+end
+
+function test_use_healing_potions_twice()
+    -- Einheit kann mehr als einen BENUTZE Heiltrank Befehl haben
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("human")
+    local u = unit.create(f, r, 60)
+    assert_equal(1200, u.hp)
+    u.hp = 400
+    turn_begin()
+    u:add_item("p14", 2)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Heiltrank")
+    u:add_order("BENUTZEN 1 Heiltrank")
+    turn_process()
+    assert_equal(1200, u.hp)
+    assert_equal(0, u:get_item("p14"))
+    turn_end()
+end
+
 function test_speedsail()
     local r = region.create(0, 0, "plain")
     local f = faction.create("human")
diff --git a/src/alchemy.c b/src/alchemy.c
index c6c550774..ec5c3dc7a 100644
--- a/src/alchemy.c
+++ b/src/alchemy.c
@@ -129,37 +129,6 @@ void herbsearch(unit * u, int max_take)
     }
 }
 
-static int begin_potion(unit * u, const item_type * itype, struct order *ord)
-{
-    static int config;
-    static bool rule_multipotion;
-
-    assert(itype);
-    if (config_changed(&config)) {
-        /* should we allow multiple different potions to be used the same turn? */
-        rule_multipotion = config_get_int("rules.magic.multipotion", 0) != 0;
-    }
-
-    if (!rule_multipotion) {
-        const item_type *use = ugetpotionuse(u);
-        if (use != NULL && use != itype) {
-            ADDMSG(&u->faction->msgs,
-                msg_message("errusingpotion", "unit using command",
-                u, use->rtype, ord));
-            return ECUSTOM;
-        }
-    }
-    return 0;
-}
-
-static void end_potion(unit * u, const item_type * itype, int amount)
-{
-    usetpotionuse(u, itype);
-
-    ADDMSG(&u->faction->msgs, msg_message("usepotion",
-        "unit potion", u, itype->rtype));
-}
-
 void show_potions(faction *f, int sklevel)
 {
     const potion_type *ptype;
@@ -229,8 +198,10 @@ static int potion_healing(struct unit *user, int amount)
 }
 
 
-static int do_potion(unit * u, region *r, const item_type * itype, int amount)
+int use_potion(unit * u, const item_type * itype, int amount, struct order *ord)
 {
+    region *r = u->region;
+
     /* TODO: this function should only be used for effect-changing potions */
     if (itype == oldpotiontype[P_PEOPLE]) {
         return potion_luck(u, r, &at_peasantluck, amount);
@@ -253,16 +224,6 @@ static int do_potion(unit * u, region *r, const item_type * itype, int amount)
     return amount;
 }
 
-int use_potion(unit * u, const item_type * itype, int amount, struct order *ord)
-{
-    int result = begin_potion(u, itype, ord);
-    if (result)
-        return result;
-    amount = do_potion(u, u->region, itype, amount);
-    end_potion(u, itype, amount);
-    return amount;
-}
-
 /*****************/
 /*   at_effect   */
 /*****************/
diff --git a/src/items.c b/src/items.c
index 5132b31cb..18c2d7b0f 100644
--- a/src/items.c
+++ b/src/items.c
@@ -303,7 +303,6 @@ struct order *ord)
     }
     use_pooled(u, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK,
         amount);
-    usetpotionuse(u, itype);
 
     ADDMSG(&u->faction->msgs, msg_message("usepotion",
         "unit potion", u, itype->rtype));
@@ -369,7 +368,6 @@ static int use_warmthpotion(unit *u, const item_type *itype,
         return ECUSTOM;
     }
     use_pooled(u, itype->rtype, GET_DEFAULT, amount);
-    usetpotionuse(u, itype);
 
     ADDMSG(&u->faction->msgs, msg_message("usepotion",
         "unit potion", u, itype->rtype));
diff --git a/src/kernel/unit.c b/src/kernel/unit.c
index 356e73354..9af46b28c 100644
--- a/src/kernel/unit.c
+++ b/src/kernel/unit.c
@@ -573,35 +573,6 @@ void usetprivate(unit * u, const char *str)
     a->data.v = str_strdup(str);
 }
 
-/*********************/
-/*   at_potionuser   */
-/*********************/
-/* Einheit BENUTZT einen Trank */
-attrib_type at_potionuser = {
-    "potionuser",
-    DEFAULT_INIT,
-    DEFAULT_FINALIZE,
-    DEFAULT_AGE,
-    NO_WRITE,
-    NO_READ
-};
-
-void usetpotionuse(unit * u, const item_type * ptype)
-{
-    attrib *a = a_find(u->attribs, &at_potionuser);
-    if (!a)
-        a = a_add(&u->attribs, a_new(&at_potionuser));
-    a->data.v = (void *)ptype;
-}
-
-const item_type *ugetpotionuse(const unit * u)
-{
-    attrib *a = a_find(u->attribs, &at_potionuser);
-    if (!a)
-        return NULL;
-    return (const item_type *)a->data.v;
-}
-
 /*********************/
 /*   at_target   */
 /*********************/
diff --git a/src/kernel/unit.h b/src/kernel/unit.h
index e7bf6a9e4..4d1cc4362 100644
--- a/src/kernel/unit.h
+++ b/src/kernel/unit.h
@@ -150,9 +150,6 @@ extern "C" {
     const char *uprivate(const struct unit *u);
     void usetprivate(struct unit *u, const char *c);
 
-    const struct item_type *ugetpotionuse(const struct unit *u);        /* benutzt u einein trank? */
-    void usetpotionuse(struct unit *u, const struct item_type *p);      /* u benutzt trank p (es darf halt nur einer pro runde) */
-
     bool ucontact(const struct unit *u, const struct unit *u2);
     void usetcontact(struct unit *u, const struct unit *c);
 

From 4a3ad7ac8425e1c1d6777fd773ecdb0ce8923b8e Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sat, 17 Feb 2018 15:22:44 +0100
Subject: [PATCH 32/40] fix potion tests, re-enable foolpotion test

---
 scripts/tests/e2/items.lua | 91 +-------------------------------------
 scripts/tests/items.lua    | 65 ++++++++++++++++++++++++---
 src/alchemy.c              |  4 +-
 3 files changed, 64 insertions(+), 96 deletions(-)

diff --git a/scripts/tests/e2/items.lua b/scripts/tests/e2/items.lua
index 85ec40e19..cefffd967 100644
--- a/scripts/tests/e2/items.lua
+++ b/scripts/tests/e2/items.lua
@@ -119,95 +119,6 @@ function test_monthly_healing()
     assert_equal(130, u.hp)
 end
 
-function test_ointment()
-    local r = region.create(0, 0, "plain")
-    local f = faction.create("human")
-    local u = unit.create(f, r, 30)
-    assert_equal(600, u.hp)
-    u.hp = 100
-    turn_begin()
-    u:add_item("ointment", 1)
-    u:clear_orders()
-    u:add_order("BENUTZEN 1 Wundsalbe")
-    turn_process()
-    assert_equal(530, u.hp)
-    assert_equal(0, u:get_item("ointment"))
-    turn_end()
-end
-
-function test_use_healing_potion()
-    -- Heiltrank kann (auch) mit BENUTZE eingesetzt werden
-    local r = region.create(0, 0, "plain")
-    local f = faction.create("human")
-    local u = unit.create(f, r, 30)
-    assert_equal(600, u.hp)
-    u.hp = 100
-    turn_begin()
-    u:add_item("p14", 1)
-    u:clear_orders()
-    u:add_order("BENUTZEN 1 Heiltrank")
-    turn_process()
-    assert_equal(530, u.hp)
-    assert_equal(0, u:get_item("p14"))
-    turn_end()
-end
-
-function test_use_healing_potion_multi_units()
-    -- Heiltrank kann mehrere Einheiten heilen
-    local r = region.create(0, 0, "plain")
-    local f = faction.create("human")
-    local u1 = unit.create(f, r, 30)
-    local u = unit.create(f, r, 30)
-    assert_equal(600, u1.hp)
-    assert_equal(600, u.hp)
-    u.hp = 400
-    u1.hp = 400
-    turn_begin()
-    u:add_item("p14", 1)
-    u:clear_orders()
-    u:add_order("BENUTZEN 1 Heiltrank")
-    turn_process()
-    assert_equal(600, u.hp)
-    assert_equal(600, u1.hp)
-    assert_equal(0, u:get_item("p14"))
-    turn_end()
-end
-
-function test_use_multiple_healing_potions()
-    -- Einheit kann mehr als einen Heiltrank benutzen
-    local r = region.create(0, 0, "plain")
-    local f = faction.create("human")
-    local u = unit.create(f, r, 60)
-    assert_equal(1200, u.hp)
-    u.hp = 400
-    turn_begin()
-    u:add_item("p14", 2)
-    u:clear_orders()
-    u:add_order("BENUTZEN 2 Heiltrank")
-    turn_process()
-    assert_equal(1200, u.hp)
-    assert_equal(0, u:get_item("p14"))
-    turn_end()
-end
-
-function test_use_healing_potions_twice()
-    -- Einheit kann mehr als einen BENUTZE Heiltrank Befehl haben
-    local r = region.create(0, 0, "plain")
-    local f = faction.create("human")
-    local u = unit.create(f, r, 60)
-    assert_equal(1200, u.hp)
-    u.hp = 400
-    turn_begin()
-    u:add_item("p14", 2)
-    u:clear_orders()
-    u:add_order("BENUTZEN 1 Heiltrank")
-    u:add_order("BENUTZEN 1 Heiltrank")
-    turn_process()
-    assert_equal(1200, u.hp)
-    assert_equal(0, u:get_item("p14"))
-    turn_end()
-end
-
 function test_speedsail()
     local r = region.create(0, 0, "plain")
     local f = faction.create("human")
@@ -227,7 +138,7 @@ function test_speedsail()
     assert_equal(1, u.ship:get_curse('shipspeed')) -- effect stays forever
 end
 
-function disable_test_foolpotion()
+function test_use_foolpotion()
     local r = region.create(0, 0, "plain")
     local f = faction.create("human")
     local u = unit.create(f, r, 1)
diff --git a/scripts/tests/items.lua b/scripts/tests/items.lua
index 153bfb803..bb910bc70 100644
--- a/scripts/tests/items.lua
+++ b/scripts/tests/items.lua
@@ -121,19 +121,74 @@ function test_antimagic()
     assert_equal(nil, r:get_curse('antimagiczone'))
 end
 
-function test_ointment()
+function test_use_healing_potion()
+    -- Heiltrank kann (auch) mit BENUTZE eingesetzt werden
     local r = region.create(0, 0, "plain")
     local f = faction.create("human")
-    local u = unit.create(f, r, 1)
-    local hp = u.hp
-    u.hp = 1
+    local u = unit.create(f, r, 30)
+    assert_equal(600, u.hp)
+    u.hp = 100
+    turn_begin()
+    u:add_item("p14", 1)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Heiltrank")
+    turn_process()
+    assert_equal(530, u.hp)
+    assert_equal(0, u:get_item("p14"))
+    turn_end()
+end
+
+function test_use_healing_potion_multi_units()
+    -- Heiltrank kann mehrere Einheiten heilen
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("human")
+    local u1 = unit.create(f, r, 30)
+    local u = unit.create(f, r, 30)
+    assert_equal(600, u1.hp)
+    assert_equal(600, u.hp)
+    u.hp = 400
+    u1.hp = 400
+    turn_begin()
+    u:add_item("p14", 1)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Heiltrank")
+    turn_process()
+    assert_equal(600, u.hp)
+    assert_equal(600, u1.hp)
+    assert_equal(0, u:get_item("p14"))
+    turn_end()
+end
+
+function test_use_multiple_healing_potions()
+    -- Einheit kann mehr als einen Heiltrank benutzen
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("human")
+    local u = unit.create(f, r, 60)
+    assert_equal(1200, u.hp)
+    u.hp = 400
+    turn_begin()
+    u:add_item("p14", 2)
+    u:clear_orders()
+    u:add_order("BENUTZEN 2 Heiltrank")
+    turn_process()
+    assert_equal(1200, u.hp)
+    assert_equal(0, u:get_item("p14"))
+    turn_end()
+end
+
+function test_use_ointment()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("human")
+    local u = unit.create(f, r, 30)
+    assert_equal(600, u.hp)
+    u.hp = 100
     u:add_item("ointment", 1)
     u:clear_orders()
     u:add_order("BENUTZEN 1 Wundsalbe")
     process_orders()
+    assert_equal(530, u.hp)
     assert_equal(0, u:get_item("ointment"))
     assert_equal(1, f:count_msg_type('usepotion'))
-    assert_equal(hp, u.hp)
 end
 
 function test_use_domore()
diff --git a/src/alchemy.c b/src/alchemy.c
index ec5c3dc7a..b6a86057e 100644
--- a/src/alchemy.c
+++ b/src/alchemy.c
@@ -202,7 +202,9 @@ int use_potion(unit * u, const item_type * itype, int amount, struct order *ord)
 {
     region *r = u->region;
 
-    /* TODO: this function should only be used for effect-changing potions */
+    ADDMSG(&u->faction->msgs, msg_message("usepotion",
+        "unit potion", u, itype->rtype));
+
     if (itype == oldpotiontype[P_PEOPLE]) {
         return potion_luck(u, r, &at_peasantluck, amount);
     }

From d065cbfca7019d3e72c685ba872c0040121ed308 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sat, 17 Feb 2018 15:30:38 +0100
Subject: [PATCH 33/40] use_item message with amount instead of usepotion
 without.

---
 res/core/messages.xml      | 16 ++++++++--------
 scripts/tests/e2/items.lua |  2 +-
 scripts/tests/items.lua    |  9 +++++----
 src/alchemy.c              | 17 +++++++++--------
 src/items.c                |  8 ++++----
 5 files changed, 27 insertions(+), 25 deletions(-)

diff --git a/res/core/messages.xml b/res/core/messages.xml
index 0c1ea60d9..4159969ee 100644
--- a/res/core/messages.xml
+++ b/res/core/messages.xml
@@ -6783,6 +6783,14 @@
     <text locale="de">"$unit($unit) in $region($region): '$order($command)' - ${error}."</text>
     <text locale="en">"$unit($unit) in $region($region): '$order($command)' - ${error}."</text>
   </message>
+  <message name="usepotion" section="events">
+    <type>
+      <arg name="unit" type="unit"/>
+      <arg name="potion" type="resource"/>
+    </type>
+    <text locale="de">"$unit($unit) benutzt $resource($potion,1)."</text>
+    <text locale="en">"$unit($unit) uses $resource($potion,1)."</text>
+  </message>
   <message name="use_item" section="events">
     <type>
       <arg name="unit" type="unit"/>
@@ -6974,14 +6982,6 @@
     <text locale="de">"$unit($unit) verdient am Handel in $region($region) Steuern in Höhe von $int($amount) Silber."</text>
     <text locale="en">"$unit($unit) collected $int($amount) silver trade tax in $region($region)."</text>
   </message>
-  <message name="usepotion" section="events">
-    <type>
-      <arg name="unit" type="unit"/>
-      <arg name="potion" type="resource"/>
-    </type>
-    <text locale="de">"$unit($unit) benutzt $resource($potion,1)."</text>
-    <text locale="en">"$unit($unit) uses $resource($potion,1)."</text>
-  </message>
   <message name="pest" section="events">
     <type>
       <arg name="dead" type="int"/>
diff --git a/scripts/tests/e2/items.lua b/scripts/tests/e2/items.lua
index cefffd967..ce7b76b40 100644
--- a/scripts/tests/e2/items.lua
+++ b/scripts/tests/e2/items.lua
@@ -38,7 +38,7 @@ function test_nestwarmth_insect()
     turn_process()
     assert_equal(flags+64, u.flags) -- UFL_WARMTH
     assert_equal(1, u:get_item("nestwarmth"))
-    assert_equal(1, f:count_msg_type('usepotion'))
+    assert_equal(1, f:count_msg_type('use_item'))
     turn_end()
 end
 
diff --git a/scripts/tests/items.lua b/scripts/tests/items.lua
index bb910bc70..9f1df3323 100644
--- a/scripts/tests/items.lua
+++ b/scripts/tests/items.lua
@@ -135,6 +135,7 @@ function test_use_healing_potion()
     turn_process()
     assert_equal(530, u.hp)
     assert_equal(0, u:get_item("p14"))
+    assert_equal(1, f:count_msg_type('use_item'))
     turn_end()
 end
 
@@ -188,7 +189,7 @@ function test_use_ointment()
     process_orders()
     assert_equal(530, u.hp)
     assert_equal(0, u:get_item("ointment"))
-    assert_equal(1, f:count_msg_type('usepotion'))
+    assert_equal(1, f:count_msg_type('use_item'))
 end
 
 function test_use_domore()
@@ -200,7 +201,7 @@ function test_use_domore()
     process_orders()
     assert_equal(10, u:effect("p3"))
     assert_equal(0, u:get_item("p3"))
-    assert_equal(1, f:count_msg_type('usepotion'))
+    assert_equal(1, f:count_msg_type('use_item'))
     u:clear_orders()
     u:set_skill('weaponsmithing', 3)
     u:add_item("iron", 2)
@@ -221,7 +222,7 @@ function test_bloodpotion_demon()
     process_orders()
     assert_equal(100, u:effect('peasantblood'))
     assert_equal(0, u:get_item("peasantblood"))
-    assert_equal(1, f:count_msg_type('usepotion'))
+    assert_equal(1, f:count_msg_type('use_item'))
     assert_equal("demon", u.race)
 end
 
@@ -235,6 +236,6 @@ function test_bloodpotion_other()
     process_orders()
     assert_equal(0, u:effect('peasantblood'))
     assert_equal(0, u:get_item("peasantblood"))
-    assert_equal(1, f:count_msg_type('usepotion'))
+    assert_equal(1, f:count_msg_type('use_item'))
     assert_equal("smurf", u.race)
 end
diff --git a/src/alchemy.c b/src/alchemy.c
index b6a86057e..2973c3748 100644
--- a/src/alchemy.c
+++ b/src/alchemy.c
@@ -202,27 +202,28 @@ int use_potion(unit * u, const item_type * itype, int amount, struct order *ord)
 {
     region *r = u->region;
 
-    ADDMSG(&u->faction->msgs, msg_message("usepotion",
-        "unit potion", u, itype->rtype));
-
     if (itype == oldpotiontype[P_PEOPLE]) {
-        return potion_luck(u, r, &at_peasantluck, amount);
+        amount = potion_luck(u, r, &at_peasantluck, amount);
     }
     else if (itype == oldpotiontype[P_HORSE]) {
-        return potion_luck(u, r, &at_horseluck, amount);
+        amount = potion_luck(u, r, &at_horseluck, amount);
     }
     else if (itype == oldpotiontype[P_HEAL]) {
-        return potion_healing(u, amount);
+        amount = potion_healing(u, amount);
     }
     else if (itype == oldpotiontype[P_OINTMENT]) {
-        return potion_ointment(u, amount);
+        amount = potion_ointment(u, amount);
     }
     else if (itype == oldpotiontype[P_MACHT]) {
-        return potion_power(u, amount);
+        amount = potion_power(u, amount);
     }
     else {
         change_effect(u, itype, 10 * amount);
     }
+    if (amount > 0) {
+        ADDMSG(&u->faction->msgs, msg_message("use_item",
+            "unit amount item", u, amount, itype->rtype));
+    }
     return amount;
 }
 
diff --git a/src/items.c b/src/items.c
index 18c2d7b0f..9376fe1c5 100644
--- a/src/items.c
+++ b/src/items.c
@@ -304,8 +304,8 @@ struct order *ord)
     use_pooled(u, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK,
         amount);
 
-    ADDMSG(&u->faction->msgs, msg_message("usepotion",
-        "unit potion", u, itype->rtype));
+    ADDMSG(&u->faction->msgs, msg_message("use_item",
+        "unit amount item", u, amount, itype->rtype));
     return 0;
 }
 
@@ -369,8 +369,8 @@ static int use_warmthpotion(unit *u, const item_type *itype,
     }
     use_pooled(u, itype->rtype, GET_DEFAULT, amount);
 
-    ADDMSG(&u->faction->msgs, msg_message("usepotion",
-        "unit potion", u, itype->rtype));
+    ADDMSG(&u->faction->msgs, msg_message("use_item",
+        "unit amount item", u, amount, itype->rtype));
     return 0;
 }
 

From ef7d0c40ed1d7f59ca4713bff9c1a582db868d04 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sat, 17 Feb 2018 18:51:11 +0100
Subject: [PATCH 34/40] remove the two helaing special cases from use_potion

---
 src/alchemy.c   | 38 ------------------------------------
 src/items.c     | 51 +++++++++++++++++++++++++++++++++++++++++++++++--
 src/laws.test.c |  2 +-
 3 files changed, 50 insertions(+), 41 deletions(-)

diff --git a/src/alchemy.c b/src/alchemy.c
index 2973c3748..b51437967 100644
--- a/src/alchemy.c
+++ b/src/alchemy.c
@@ -166,38 +166,6 @@ static int potion_power(unit *u, int amount) {
     return amount;
 }
 
-static int heal(unit * user, int effect)
-{
-    int req = unit_max_hp(user) * user->number - user->hp;
-    if (req > 0) {
-        if (req > effect) req = effect;
-        effect -= req;
-        user->hp += req;
-    }
-    return effect;
-}
-
-static int potion_ointment(unit * u, int amount) {
-    int effect = amount * 400;
-    effect = heal(u, effect);
-    return amount;
-}
-
-static int potion_healing(struct unit *user, int amount)
-{
-    int effect = amount * 400;
-    unit *u = user->region->units;
-    effect = heal(user, effect);
-    while (effect > 0 && u != NULL) {
-        if (u->faction == user->faction) {
-            effect = heal(u, effect);
-        }
-        u = u->next;
-    }
-    return amount;
-}
-
-
 int use_potion(unit * u, const item_type * itype, int amount, struct order *ord)
 {
     region *r = u->region;
@@ -208,12 +176,6 @@ int use_potion(unit * u, const item_type * itype, int amount, struct order *ord)
     else if (itype == oldpotiontype[P_HORSE]) {
         amount = potion_luck(u, r, &at_horseluck, amount);
     }
-    else if (itype == oldpotiontype[P_HEAL]) {
-        amount = potion_healing(u, amount);
-    }
-    else if (itype == oldpotiontype[P_OINTMENT]) {
-        amount = potion_ointment(u, amount);
-    }
     else if (itype == oldpotiontype[P_MACHT]) {
         amount = potion_power(u, amount);
     }
diff --git a/src/items.c b/src/items.c
index 9376fe1c5..6f1981a08 100644
--- a/src/items.c
+++ b/src/items.c
@@ -411,6 +411,53 @@ static int use_water_of_life(unit *u, const item_type *itype,
     return potion_water_of_life(u, u->region, amount);
 }
 
+static int heal(unit * user, int effect)
+{
+    int req = unit_max_hp(user) * user->number - user->hp;
+    if (req > 0) {
+        if (req > effect) req = effect;
+        effect -= req;
+        user->hp += req;
+    }
+    return effect;
+}
+
+static int potion_healing(struct unit *user, int amount)
+{
+    int effect = amount * 400;
+    unit *u = user->region->units;
+    effect = heal(u, effect);
+    while (effect > 0 && u != NULL) {
+        if (u->faction == user->faction) {
+            effect = heal(u, effect);
+        }
+        u = u->next;
+    }
+    return amount;
+}
+
+static int use_healing_potion(unit *u, const item_type *itype,
+    int amount, struct order *ord)
+{
+    ADDMSG(&u->faction->msgs, msg_message("use_item",
+        "unit amount item", u, amount, itype->rtype));
+    return potion_healing(u, amount);
+}
+
+static int potion_ointment(unit * u, int amount) {
+    int effect = amount * 400;
+    effect = heal(u, effect);
+    return amount;
+}
+
+static int use_ointment(unit *u, const item_type *itype,
+    int amount, struct order *ord)
+{
+    ADDMSG(&u->faction->msgs, msg_message("use_item",
+        "unit amount item", u, amount, itype->rtype));
+    return potion_ointment(u, amount);
+}
+
 void register_itemfunctions(void)
 {
     /* have tests: */
@@ -425,7 +472,7 @@ void register_itemfunctions(void)
     register_item_use(use_birthdayamulet, "use_aoc");
     register_item_use(use_foolpotion, "use_p7");
     register_item_use(use_bloodpotion, "use_peasantblood");
-    register_item_use(use_potion, "use_ointment");
-    register_item_use(use_potion, "use_p14");
+    register_item_use(use_ointment, "use_ointment");
+    register_item_use(use_healing_potion, "use_p14");
     register_item_use(use_warmthpotion, "use_nestwarmth");
 }
diff --git a/src/laws.test.c b/src/laws.test.c
index 888614a1a..4d641cc52 100644
--- a/src/laws.test.c
+++ b/src/laws.test.c
@@ -883,7 +883,7 @@ static void test_luck_message(CuTest *tc) {
 
     demographics();
 
-    CuAssertPtrEquals_Msg(tc, "unexpected message", (void *)NULL, r->msgs);
+    CuAssertPtrEquals_Msg(tc, "unexpected message", NULL, r->msgs);
 
     a = (attrib *)a_find(r->attribs, &at_peasantluck);
     if (!a)

From afe1cf34f3bb1f6910ba287b7326f4aa2b381d07 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sat, 17 Feb 2018 18:59:43 +0100
Subject: [PATCH 35/40] rough acceptance test for elixir of power (p13)

---
 scripts/tests/items.lua | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/scripts/tests/items.lua b/scripts/tests/items.lua
index 9f1df3323..9d63b875d 100644
--- a/scripts/tests/items.lua
+++ b/scripts/tests/items.lua
@@ -177,6 +177,21 @@ function test_use_multiple_healing_potions()
     turn_end()
 end
 
+function test_use_elixir()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("human")
+    local u = unit.create(f, r, 10)
+    assert_equal(200, u.hp)
+    u:add_item("p13", 1)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Elixier~der~Macht")
+    process_orders()
+    -- potion makes hp 1000, monthly_healing takes away 400:
+    assert_equal(600, u.hp)
+    assert_equal(0, u:get_item("p13"))
+    assert_equal(1, f:count_msg_type('use_item'))
+end
+
 function test_use_ointment()
     local r = region.create(0, 0, "plain")
     local f = faction.create("human")

From b602ac5f1a5578e98ae45c6a4524fcbd440cc722 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sat, 17 Feb 2018 19:02:02 +0100
Subject: [PATCH 36/40] remove P_POWER from use_potion

---
 src/alchemy.c | 14 --------------
 src/items.c   | 20 ++++++++++++++++++++
 2 files changed, 20 insertions(+), 14 deletions(-)

diff --git a/src/alchemy.c b/src/alchemy.c
index b51437967..4b7e961a6 100644
--- a/src/alchemy.c
+++ b/src/alchemy.c
@@ -155,17 +155,6 @@ static int potion_luck(unit *u, region *r, attrib_type *atype, int amount) {
     return amount;
 }
 
-static int potion_power(unit *u, int amount) {
-    int hp = 10 * amount;
-
-    if (hp > u->number) {
-        hp = u->number;
-        amount = (hp + 9) % 10;
-    }
-    u->hp += hp * unit_max_hp(u) * 4;
-    return amount;
-}
-
 int use_potion(unit * u, const item_type * itype, int amount, struct order *ord)
 {
     region *r = u->region;
@@ -176,9 +165,6 @@ int use_potion(unit * u, const item_type * itype, int amount, struct order *ord)
     else if (itype == oldpotiontype[P_HORSE]) {
         amount = potion_luck(u, r, &at_horseluck, amount);
     }
-    else if (itype == oldpotiontype[P_MACHT]) {
-        amount = potion_power(u, amount);
-    }
     else {
         change_effect(u, itype, 10 * amount);
     }
diff --git a/src/items.c b/src/items.c
index 6f1981a08..41617c0d0 100644
--- a/src/items.c
+++ b/src/items.c
@@ -458,6 +458,25 @@ static int use_ointment(unit *u, const item_type *itype,
     return potion_ointment(u, amount);
 }
 
+static int potion_power(unit *u, int amount) {
+    int hp = 10 * amount;
+
+    if (hp > u->number) {
+        hp = u->number;
+        amount = (hp + 9) % 10;
+    }
+    u->hp += hp * unit_max_hp(u) * 4;
+    return amount;
+}
+
+static int use_power_elixir(unit *u, const item_type *itype,
+    int amount, struct order *ord)
+{
+    ADDMSG(&u->faction->msgs, msg_message("use_item",
+        "unit amount item", u, amount, itype->rtype));
+    return potion_power(u, amount);
+}
+
 void register_itemfunctions(void)
 {
     /* have tests: */
@@ -474,5 +493,6 @@ void register_itemfunctions(void)
     register_item_use(use_bloodpotion, "use_peasantblood");
     register_item_use(use_ointment, "use_ointment");
     register_item_use(use_healing_potion, "use_p14");
+    register_item_use(use_power_elixir, "use_p13");
     register_item_use(use_warmthpotion, "use_nestwarmth");
 }

From 6c572c6287f53f3261176329b83eead7911478ea Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sat, 17 Feb 2018 20:35:00 +0100
Subject: [PATCH 37/40] rename potion: p14 to healing fix use_item message
 crash

---
 res/core/common/potions.xml    |  2 +-
 res/core/de/strings.xml        |  6 +++---
 res/core/en/strings.xml        |  4 ++--
 res/core/fr/strings.xml        |  4 ++--
 res/core/messages.xml          |  6 +++---
 res/e3a/items.xml              |  2 +-
 res/e3a/spellbooks/cerddor.xml |  2 +-
 res/e3a/spellbooks/draig.xml   |  2 +-
 res/e3a/spellbooks/gray.xml    |  2 +-
 res/e3a/spellbooks/gwyrrd.xml  |  2 +-
 res/e3a/spellbooks/illaun.xml  |  2 +-
 res/e3a/spells.xml             |  2 +-
 res/e3a/strings.xml            |  4 ++--
 scripts/eressea/spells.lua     |  4 ++--
 scripts/tests/items.lua        | 12 ++++++------
 src/alchemy.h                  |  1 -
 src/items.c                    |  2 +-
 src/kernel/item.c              | 16 +++++++++++-----
 18 files changed, 40 insertions(+), 35 deletions(-)

diff --git a/res/core/common/potions.xml b/res/core/common/potions.xml
index ab2b8c951..fa3ce3478 100644
--- a/res/core/common/potions.xml
+++ b/res/core/common/potions.xml
@@ -167,7 +167,7 @@
     </item>
   </resource>
 
-  <resource name="p14" appearance="vial">
+  <resource name="healing" appearance="vial">
     <item weight="0" score="120" use="yes">
       <potion level="4"/>
       <construction skill="alchemy" minskill="8">
diff --git a/res/core/de/strings.xml b/res/core/de/strings.xml
index 28e524b38..21383e1af 100644
--- a/res/core/de/strings.xml
+++ b/res/core/de/strings.xml
@@ -1899,7 +1899,7 @@
   <string name="p13">
     <text locale="de">Elixier der Macht</text>
   </string>
-  <string name="p14">
+  <string name="healing">
     <text locale="de">Heiltrank</text>
   </string>
 
@@ -1945,7 +1945,7 @@
   <string name="p13_p">
     <text locale="de">Elixiere der Macht</text>
   </string>
-  <string name="p14_p">
+  <string name="healing_p">
     <text locale="de">Heiltränke</text>
   </string>
 
@@ -3640,7 +3640,7 @@
       <text locale="en">One of the most rare and prized of all alchemist elixers, this potion grants the user a dragon's power for a few weeks. The potion increases the life-energy of a maximum of ten people fivefold. The effect is strongest right after drinking and slowly decreases over time. To brew this potion the alchemist needs an elvendear, a windbag, a piece of waterfinder and a spider ivy. Finally he dusts it with some minced bubblemorel and stirrs the powder into some dragon's blood. </text>
       <text locale="de">Eines der seltensten und wertvollsten alchemistischen Elixiere, verleiht dieser Trank dem Anwender für einige Wochen die Kraft eines Drachen. Der Trank erhöht die Lebensenergie von maximal zehn Personen auf das fünffache. Die Wirkung ist direkt nach der Einnahme am stärksten und klingt danach langsam ab. Zur Herstellung benötigt der Alchemist ein Elfenlieb, einen Windbeutel, ein Stück Wasserfinder und einen Grünen Spinnerich. Über dieses Mischung streue er schließlich einen zerriebenen Blasenmorchel und rühre dieses Pulver unter etwas Drachenblut.</text>
     </string>
-    <string name="p14">
+    <string name="healing">
       <text locale="en">For a healing potion one takes the peel of a windbag and some bugleweed, stirr in some chopped elvendear and sprinkle it with the blossoms of an ice begonia. This has to cook through for four days, while a gapgrowth has to be added on the second day. Then one carefully scoops off the top layer of liquid. One such potion gives four men (or one man four times) a 50% chance to survive otherwise lethal wounds. The potion is automatically used in case of injury.</text>
       <text locale="de">Für einen Heiltrank nehme man die Schale eines Windbeutels und etwas Gurgelkraut, rühre eine kleingehacktes Elfenlieb dazu und bestreue alles mit den Blüten einer Eisblume. Dies muß vier Tage lang gären, wobei man am zweiten Tag einen Spaltwachs dazutun muß. Dann ziehe man vorsichtig den oben schwimmenden Saft ab. Ein solcher Trank gibt vier Männern (oder einem Mann vier mal) im Kampf eine Chance von 50%, sonst tödliche Wunden zu überleben. Der Trank wird von ihnen automatisch bei Verletzung angewandt.</text>
     </string>
diff --git a/res/core/en/strings.xml b/res/core/en/strings.xml
index 5324b165d..0347e92b5 100644
--- a/res/core/en/strings.xml
+++ b/res/core/en/strings.xml
@@ -1150,10 +1150,10 @@
   <string name="p13_p">
     <text locale="en">elixirs of power</text>
   </string>
-  <string name="p14">
+  <string name="healing">
     <text locale="en">healing potion</text>
   </string>
-  <string name="p14_p">
+  <string name="healing_p">
     <text locale="en">healing potions</text>
   </string>
 
diff --git a/res/core/fr/strings.xml b/res/core/fr/strings.xml
index f81b5fc96..37877b57b 100644
--- a/res/core/fr/strings.xml
+++ b/res/core/fr/strings.xml
@@ -1157,10 +1157,10 @@
   <string name="p13_p">
     <text locale="fr">elixir d'endurance</text>
   </string>
-  <string name="p14">
+  <string name="healing">
     <text locale="fr">potion de survie</text>
   </string>
-  <string name="p14_p">
+  <string name="healing_p">
     <text locale="fr">potions de survie</text>
   </string>
 
diff --git a/res/core/messages.xml b/res/core/messages.xml
index 4159969ee..b810b8a43 100644
--- a/res/core/messages.xml
+++ b/res/core/messages.xml
@@ -2800,7 +2800,7 @@
     <text locale="de">"$unit($unit) in $region($region) produziert $int($amount)$if($eq($wanted,$amount),""," von $int($wanted)") $resource($resource,$wanted)."</text>
     <text locale="en">"$unit($unit) in $region($region) produces $int($amount)$if($eq($wanted,$amount),""," of $int($wanted)") $resource($resource,$amount)."</text>
   </message>
-  <message name="buildbuilding" section="production">
+    <message name="buildbuilding" section="production">
     <type>
       <arg name="unit" type="unit"/>
       <arg name="size" type="int"/>
@@ -6797,8 +6797,8 @@
       <arg name="item" type="resource"/>
       <arg name="amount" type="int"/>
     </type>
-    <text locale="de">"$unit($unit) benutzt $amount $resource($item,$amount)."</text>
-    <text locale="en">"$unit($unit) uses $amount $resource($item,$amount)."</text>
+    <text locale="de">"$unit($unit) benutzt $int($amount) $resource($item,$amount)."</text>
+    <text locale="en">"$unit($unit) uses $int($amount) $resource($item,$amount)."</text>
   </message>
   <message name="no_attack_after_advance" section="errors">
     <type>
diff --git a/res/e3a/items.xml b/res/e3a/items.xml
index 8f84796be..42d425b24 100644
--- a/res/e3a/items.xml
+++ b/res/e3a/items.xml
@@ -52,7 +52,7 @@
     </item>
   </resource>
 
-  <resource name="p14" appearance="vial">
+  <resource name="healing" appearance="vial">
     <!-- Heiltrank -->
     <item weight="0" score="120" use="yes">
       <potion level="4"/>
diff --git a/res/e3a/spellbooks/cerddor.xml b/res/e3a/spellbooks/cerddor.xml
index 9e1a3f835..b52fe5d6f 100644
--- a/res/e3a/spellbooks/cerddor.xml
+++ b/res/e3a/spellbooks/cerddor.xml
@@ -7,7 +7,7 @@
   <entry spell="create_potion_peasantblood" level="5" />
   <entry spell="create_potion_ointment" level="6" />
   <entry spell="create_potion_p3" level="7" />
-  <entry spell="create_potion_p14" level="8" />
+  <entry spell="create_potion_healing" level="8" />
   <entry spell="create_potion_p13" level="9" />
   <entry spell="create_roi" level="7" />
   <entry spell="create_aots" level="6" />
diff --git a/res/e3a/spellbooks/draig.xml b/res/e3a/spellbooks/draig.xml
index a0da7b449..2d29b44ca 100644
--- a/res/e3a/spellbooks/draig.xml
+++ b/res/e3a/spellbooks/draig.xml
@@ -7,7 +7,7 @@
   <entry spell="create_potion_peasantblood" level="5" />
   <entry spell="create_potion_ointment" level="6" />
   <entry spell="create_potion_p3" level="7" />
-  <entry spell="create_potion_p14" level="8" />
+  <entry spell="create_potion_healing" level="8" />
   <entry spell="create_potion_p13" level="9" />
   <entry spell="create_roi" level="7" />
   <entry spell="create_aots" level="6" />
diff --git a/res/e3a/spellbooks/gray.xml b/res/e3a/spellbooks/gray.xml
index a807963c2..998405b03 100644
--- a/res/e3a/spellbooks/gray.xml
+++ b/res/e3a/spellbooks/gray.xml
@@ -38,7 +38,7 @@
     <entry spell="create_potion_ointment" level="6" />
     <entry spell="create_potion_p0" level="3" />
     <entry spell="create_potion_p13" level="9" />
-    <entry spell="create_potion_p14" level="8" />
+    <entry spell="create_potion_healing" level="8" />
     <entry spell="create_potion_p2" level="2" />
     <entry spell="create_potion_p3" level="7" />
     <entry spell="create_potion_p9" level="4" />
diff --git a/res/e3a/spellbooks/gwyrrd.xml b/res/e3a/spellbooks/gwyrrd.xml
index 83fd4e99c..76cf7a154 100644
--- a/res/e3a/spellbooks/gwyrrd.xml
+++ b/res/e3a/spellbooks/gwyrrd.xml
@@ -7,7 +7,7 @@
   <entry spell="create_potion_peasantblood" level="5" />
   <entry spell="create_potion_ointment" level="6" />
   <entry spell="create_potion_p3" level="7" />
-  <entry spell="create_potion_p14" level="8" />
+  <entry spell="create_potion_healing" level="8" />
   <entry spell="create_potion_p13" level="9" />
   <entry spell="create_roi" level="7" />
   <entry spell="create_aots" level="6" />
diff --git a/res/e3a/spellbooks/illaun.xml b/res/e3a/spellbooks/illaun.xml
index 02c9b34d8..0392842c2 100644
--- a/res/e3a/spellbooks/illaun.xml
+++ b/res/e3a/spellbooks/illaun.xml
@@ -7,7 +7,7 @@
   <entry spell="create_potion_peasantblood" level="5" />
   <entry spell="create_potion_ointment" level="6" />
   <entry spell="create_potion_p3" level="7" />
-  <entry spell="create_potion_p14" level="8" />
+  <entry spell="create_potion_healing" level="8" />
   <entry spell="create_potion_p13" level="9" />
   <entry spell="create_roi" level="7" />
   <entry spell="create_aots" level="6" />
diff --git a/res/e3a/spells.xml b/res/e3a/spells.xml
index 25d123d43..f3edbd9f6 100644
--- a/res/e3a/spells.xml
+++ b/res/e3a/spells.xml
@@ -421,7 +421,7 @@
     <resource name="h20" amount="1" cost="linear"/><!-- Schneekristall -->
     <resource name="h9" amount="1" cost="linear"/><!-- Wasserfinder -->
   </spell>
-  <spell name="create_potion_p14" ship="true" rank="5" variable="true">
+  <spell name="create_potion_healing" ship="true" rank="5" variable="true">
     <!-- Heiltrank -->
     <resource name="aura" amount="5" cost="linear"/>
     <resource name="h0" amount="1" cost="linear"/><!-- Flachwurz -->
diff --git a/res/e3a/strings.xml b/res/e3a/strings.xml
index f68720e6a..4a089cb50 100644
--- a/res/e3a/strings.xml
+++ b/res/e3a/strings.xml
@@ -197,7 +197,7 @@
       <text locale="de">Braue Elixier der Macht</text>
       <text locale="en">brew elixir of power</text>
     </string>
-    <string name="create_potion_p14">
+    <string name="create_potion_healing">
       <text locale="de">Braue Heiltrank</text>
       <text locale="en">brew healing potion</text>
     </string>
@@ -264,7 +264,7 @@
       <text locale="en">Just like with the knowledge about death, the peasants feel uncomfortable with the knowledge about monsters. A few warriors though, who have already faced these creatures in combat, found that the monsters blood had an invigourating effect on them. There is talk about some warriors who bathed in the blood of the slain monsters to take up their strength. But this effect ends soon, and only occurs with fresh blood. As no one has time to quickly slay a wyrm before attacking their neighbors, a way had to be found to make the effect last longer. After lots of experiments that cost the life of lots of good warriors who had to constantly bring in fresh dragon blood, Manasouf the black finally found a way. Originally a closely guarded secret, the recipe is now known in all lands. First, the hardened dragon blood needs to be melted in hot tin. After that, the magician binds the spirit of the dragon to its blood once again. It can not find eternal rest until the last bit of blood has been used. </text>
       <text locale="de">Ebenso wie das Wissen über den Tod ist das Wissen über gewisse Monster bei der abergläubigen Bevölkerung nicht gerne gesehen. Einige wenige Krieger jedoch, die diesen Kreaturen schon mal im Kampf gegenüberstanden, haben entdeckt, dass deren Blut eine belebende Wirkung auf sie hatte. So soll es schon Krieger gegeben haben, die im Blut der erschlagenen Monster badeten, um deren Stärke in sich aufzunehmen. Diese Wirkung verfliegt jedoch rasch und wirkt nur bei frischen Blut. Da niemand vor dem Kampf gegen seinen Nachbarn die Zeit hat, schnell noch einen Wyrm zu erschlagen, musste ein Weg gefunden werden, die Wirkung haltbar zu machen. Manasouf dem Schwarzen gelang dies nach zahlreichen Experimenten, die das Leben vieler guter Männer kosteten, welche ständig neues Drachenblut für seine Versuche beschaffen mussten. Ursprünglich ein streng gehütetes Geheimnis ist das Rezept inzwischen im ganzen Land bekannt. Zunächst muss geronnenes Drachenblut in heißem Zinn verflüssigt werden. Anschließend wird der Geist des erschlagenen Drachen in der Geisterebene wieder an sein Blut gebunden und kann so lange nicht in Frieden ruhen, bis das letzte bisschen seines Blutes verbraucht wurde.</text>
     </string>
-    <string name="create_potion_p14">
+    <string name="create_potion_healing">
       <text locale="en">Some mages research death's secrets until they can bring the dead back to life. But those who are brought back are often only shadows of ther former self and turn against their erstwhile friends. But those mages that study life and its iteraction with death find a possibility to bring the deceased back as their original selves. A drawback is that this is only possible in the very first minutes after death. As even mages can not be everywhere at the same time, a way had to be found to give this ability to helpers. All healers who tried to learn this from the mages failed, though, until one of those healers was backstabbingly killed. In the moment of his death he used the knowledge gained and was able to have his murderer executed the following day. The potion he designed has to be blessed by a magician before usage at any given time. This potion gives four people (or one person four times) a 50% chance to survive an otherwise deadly wound. It is used automatically by the victom.</text>
       <text locale="de">Manche Magier erforschen den Tod, bis sie Verstorbene wieder ins Leben zurück bringen können. Diese sind jedoch meist bösartig und nur noch Schatten ihres früheren Selbst. Diejenigen jedoch, die sich intensiv mit dem Leben und seiner Kombination mit dem Tod beschäftigen, finden eine Möglichkeit, Verstorbene in ihrer wahren Gestalt zurück zu rufen. Dies ist allerdings nur wenige Minuten nach dem Tod möglich. Da selbst Magier nicht überall gleichzeitig sein können, musste ein Weg gefunden werden, diese Fähigkeit auf andere zu übertragen. Alle Versuche, dies Feldschern beizubringen, scheiterten jedoch, bis einer dieser Feldscher von einem Widersacher hinterrücks ermordet wurde. Im Moment seines Todes wandte er sein erworbenes Wissen an und konnte tags darauf den Übeltäter wegen Mordes hinrichten lassen. Der von ihm entwickelte magische Trank muss jedoch von einem der Magie des Lebens Kundigen gesegnet werden, um seine volle Wirkung zu entfalten. Ein solcher Trank gibt vier Männern (oder einem Mann viermal) im Kampf eine Chance von 50%, sonst tödliche Wunden zu überleben. Der Trank wird von ihnen automatisch bei Verletzung angewandt.</text>
     </string>
diff --git a/scripts/eressea/spells.lua b/scripts/eressea/spells.lua
index 5f1507d41..eb33dd95f 100644
--- a/scripts/eressea/spells.lua
+++ b/scripts/eressea/spells.lua
@@ -55,8 +55,8 @@ function create_potion_p3(r, mage, level, force)
 end
 
 -- Heiltrank
-function create_potion_p14(r, mage, level, force)
-  return create_potion(mage, level, "p14", force)
+function create_potion_healing(r, mage, level, force)
+  return create_potion(mage, level, "healing", force)
 end
 
 -- Elixier der Macht
diff --git a/scripts/tests/items.lua b/scripts/tests/items.lua
index 9d63b875d..60bbf100f 100644
--- a/scripts/tests/items.lua
+++ b/scripts/tests/items.lua
@@ -129,12 +129,12 @@ function test_use_healing_potion()
     assert_equal(600, u.hp)
     u.hp = 100
     turn_begin()
-    u:add_item("p14", 1)
+    u:add_item("healing", 1)
     u:clear_orders()
     u:add_order("BENUTZEN 1 Heiltrank")
     turn_process()
     assert_equal(530, u.hp)
-    assert_equal(0, u:get_item("p14"))
+    assert_equal(0, u:get_item("healing"))
     assert_equal(1, f:count_msg_type('use_item'))
     turn_end()
 end
@@ -150,13 +150,13 @@ function test_use_healing_potion_multi_units()
     u.hp = 400
     u1.hp = 400
     turn_begin()
-    u:add_item("p14", 1)
+    u:add_item("healing", 1)
     u:clear_orders()
     u:add_order("BENUTZEN 1 Heiltrank")
     turn_process()
     assert_equal(600, u.hp)
     assert_equal(600, u1.hp)
-    assert_equal(0, u:get_item("p14"))
+    assert_equal(0, u:get_item("healing"))
     turn_end()
 end
 
@@ -168,12 +168,12 @@ function test_use_multiple_healing_potions()
     assert_equal(1200, u.hp)
     u.hp = 400
     turn_begin()
-    u:add_item("p14", 2)
+    u:add_item("healing", 2)
     u:clear_orders()
     u:add_order("BENUTZEN 2 Heiltrank")
     turn_process()
     assert_equal(1200, u.hp)
-    assert_equal(0, u:get_item("p14"))
+    assert_equal(0, u:get_item("healing"))
     turn_end()
 end
 
diff --git a/src/alchemy.h b/src/alchemy.h
index 3ce6b9547..b1dde1c44 100644
--- a/src/alchemy.h
+++ b/src/alchemy.h
@@ -52,7 +52,6 @@ extern "C" {
         /* Stufe 4 */
         P_PEOPLE,
         P_WAHRHEIT,
-        P_MACHT,
         P_HEAL,
         MAX_POTIONS
     };
diff --git a/src/items.c b/src/items.c
index 41617c0d0..e162d8fe2 100644
--- a/src/items.c
+++ b/src/items.c
@@ -492,7 +492,7 @@ void register_itemfunctions(void)
     register_item_use(use_foolpotion, "use_p7");
     register_item_use(use_bloodpotion, "use_peasantblood");
     register_item_use(use_ointment, "use_ointment");
-    register_item_use(use_healing_potion, "use_p14");
+    register_item_use(use_healing_potion, "use_healing");
     register_item_use(use_power_elixir, "use_p13");
     register_item_use(use_warmthpotion, "use_nestwarmth");
 }
diff --git a/src/kernel/item.c b/src/kernel/item.c
index db551bed3..4483c51f0 100644
--- a/src/kernel/item.c
+++ b/src/kernel/item.c
@@ -206,12 +206,13 @@ resource_type *rt_get_or_create(const char *name) {
 
 static const char *it_aliases[][2] = {
     { "Runenschwert", "runesword" },
-    { "p12", "truthpotion" },
     { "p1", "goliathwater" },
     { "p2", "lifepotion" },
     { "p4", "ointment" },
     { "p5", "peasantblood" },
     { "p8", "nestwarmth" },
+    { "p14", "healing" },
+    { "p12", "truthpotion" },
     { "diamond", "adamantium" },
     { "diamondaxe", "adamantiumaxe" },
     { "diamondplate", "adamantiumplate" },
@@ -226,13 +227,18 @@ static const char *it_alias(const char *zname)
         if (strcmp(it_aliases[i][0], zname) == 0)
             return it_aliases[i][1];
     }
-    return zname;
+    return NULL;
 }
 
 item_type *it_find(const char *zname)
 {
-    const char *name = it_alias(zname);
-    resource_type *result = rt_find(name);
+    resource_type *result = rt_find(zname);
+    if (!result) {
+        const char *name = it_alias(zname);
+        if (name) {
+            result = rt_find(name);
+        }
+    }
     return result ? result->itype : 0;
 }
 
@@ -638,7 +644,7 @@ static void init_oldpotions(void)
 {
     const char *potionnames[MAX_POTIONS] = {
         "p0", "goliathwater", "lifepotion", "p3", "ointment", "peasantblood", "p6",
-        "p7", "nestwarmth", "p9", "p10", "p11", "truthpotion", "p13", "p14"
+        "p7", "nestwarmth", "p9", "p10", "p11", "truthpotion", "healing"
     };
     int p;
 

From 06f3e337b6e96b57bb565f5d010b9260c8920f11 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sat, 17 Feb 2018 20:56:40 +0100
Subject: [PATCH 38/40] missing plural for lifepotion

---
 res/core/de/strings.xml | 2 +-
 res/core/fr/strings.xml | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/res/core/de/strings.xml b/res/core/de/strings.xml
index 21383e1af..98f047a39 100644
--- a/res/core/de/strings.xml
+++ b/res/core/de/strings.xml
@@ -1909,7 +1909,7 @@
   <string name="goliathwater_p">
     <text locale="de">Goliathwasser</text>
   </string>
-  <string name="p2_p">
+  <string name="lifepotion_p">
     <text locale="de">Wasser des Lebens</text>
   </string>
   <string name="p3_p">
diff --git a/res/core/fr/strings.xml b/res/core/fr/strings.xml
index 37877b57b..d6c61e00c 100644
--- a/res/core/fr/strings.xml
+++ b/res/core/fr/strings.xml
@@ -1088,7 +1088,7 @@
   <string name="lifepotion">
     <text locale="fr">élixir de vie</text>
   </string>
-  <string name="p2_p">
+  <string name="lifepotion_p">
     <text locale="fr">élixir de vie</text>
   </string>
   <string name="p3">

From cb537b1a9316516567d3fa47283ad4b866265bff Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sat, 17 Feb 2018 21:08:58 +0100
Subject: [PATCH 39/40] missing translations are the worst.

---
 res/core/en/strings.xml | 2 +-
 src/reports.c           | 8 ++++++--
 src/util/language.c     | 2 +-
 3 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/res/core/en/strings.xml b/res/core/en/strings.xml
index 0347e92b5..096dace78 100644
--- a/res/core/en/strings.xml
+++ b/res/core/en/strings.xml
@@ -1081,7 +1081,7 @@
   <string name="lifepotion">
     <text locale="en">water of life</text>
   </string>
-  <string name="p2_p">
+  <string name="lifepotion_p">
     <text locale="en">waters of life</text>
   </string>
   <string name="p3">
diff --git a/src/reports.c b/src/reports.c
index 20f2068e8..992e6de9f 100644
--- a/src/reports.c
+++ b/src/reports.c
@@ -2114,10 +2114,14 @@ static void eval_resource(struct opstack **stack, const void *userdata)
     int j = opop(stack).i;
     const struct resource_type *res = (const struct resource_type *)opop(stack).v;
     const char *c = LOC(lang, resourcename(res, j != 1));
-    size_t len = strlen(c);
     variant var;
+    if (c) {
+        size_t len = strlen(c);
 
-    var.v = strcpy(balloc(len + 1), c);
+        var.v = strcpy(balloc(len + 1), c);
+    } else {
+        var.v = NULL;
+    }
     opush(stack, var);
 }
 
diff --git a/src/util/language.c b/src/util/language.c
index cf76dc4de..b2447b695 100644
--- a/src/util/language.c
+++ b/src/util/language.c
@@ -186,7 +186,7 @@ const char *locale_string(const locale * lang, const char *key, bool warn)
             return value;
         }
     }
-    return 0;
+    return NULL;
 }
 
 void locale_setstring(locale * lang, const char *key, const char *value)

From f9fcc0182982af75c389d29411a1d3331ea32640 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sat, 17 Feb 2018 21:14:47 +0100
Subject: [PATCH 40/40] print more debug information when a name is missing.

---
 src/reports.c | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/src/reports.c b/src/reports.c
index 992e6de9f..69e7a3555 100644
--- a/src/reports.c
+++ b/src/reports.c
@@ -2113,13 +2113,15 @@ static void eval_resource(struct opstack **stack, const void *userdata)
     const struct locale *lang = report ? report->locale : default_locale;
     int j = opop(stack).i;
     const struct resource_type *res = (const struct resource_type *)opop(stack).v;
-    const char *c = LOC(lang, resourcename(res, j != 1));
+    const char *name = resourcename(res, j != 1);
+    const char *c = LOC(lang, name);
     variant var;
     if (c) {
         size_t len = strlen(c);
 
         var.v = strcpy(balloc(len + 1), c);
     } else {
+        log_error("missing translation for %s in eval_resource", name);
         var.v = NULL;
     }
     opush(stack, var);
@@ -2131,11 +2133,17 @@ static void eval_race(struct opstack **stack, const void *userdata)
     const struct locale *lang = report ? report->locale : default_locale;
     int j = opop(stack).i;
     const race *r = (const race *)opop(stack).v;
-    const char *c = LOC(lang, rc_name_s(r, (j == 1) ? NAME_SINGULAR : NAME_PLURAL));
-    size_t len = strlen(c);
+    const char *name = rc_name_s(r, (j == 1) ? NAME_SINGULAR : NAME_PLURAL);
+    const char *c = LOC(lang, name);
     variant var;
-
-    var.v = strcpy(balloc(len + 1), c);
+    if (c) {
+        size_t len = strlen(c);
+        var.v = strcpy(balloc(len + 1), c);
+    }
+    else {
+        log_error("missing translation for %s in eval_race", name);
+        var.v = NULL;
+    }
     opush(stack, var);
 }