From 0738090f281811d72d4a55aafebeae34545b0274 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sun, 26 Feb 2017 13:19:47 +0100
Subject: [PATCH 01/42] no more funpointers in resource_limit. change how
 resource limits in lua are called.

---
 src/economy.c          | 34 +++++++++++++---------------------
 src/helpers.c          | 11 +++++------
 src/kernel/resources.c | 21 +++++++++++++++++++++
 src/kernel/resources.h | 13 ++++++-------
 src/kernel/xmlreader.c | 35 -----------------------------------
 5 files changed, 45 insertions(+), 69 deletions(-)

diff --git a/src/economy.c b/src/economy.c
index f9ded29de..b1b9afb32 100644
--- a/src/economy.c
+++ b/src/economy.c
@@ -914,10 +914,6 @@ struct message * get_modifiers(unit *u, const resource_mod *mod, variant *savep,
     return NULL;
 }
 
-static resource_limit *get_resourcelimit(const resource_type *rtype) {
-    return rtype->limit;
-}
-
 static void allocate_resource(unit * u, const resource_type * rtype, int want)
 {
     const item_type *itype = resource2item(rtype);
@@ -925,7 +921,7 @@ static void allocate_resource(unit * u, const resource_type * rtype, int want)
     int dm = 0;
     allocation_list *alist;
     allocation *al;
-    resource_limit *rdata = get_resourcelimit(rtype);
+    resource_limit *rdata = rtype->limit;
     const resource_type *rring;
     int amount, skill, skill_mod = 0;
     variant save_mod;
@@ -936,8 +932,8 @@ static void allocate_resource(unit * u, const resource_type * rtype, int want)
         || itype->construction->materials == NULL));
     assert(rdata != NULL);
 
-    if (rdata->limit != NULL) {
-        int avail = rdata->limit(r, rtype);
+    if (!rtype->raw) {
+        int avail = limit_resource(r, rtype);
         if (avail <= 0) {
             cmistake(u, u->thisorder, 121, MSG_PRODUCE);
             return;
@@ -1122,17 +1118,17 @@ attrib_allocation(const resource_type * rtype, region * r, allocation * alist)
 {
     allocation *al;
     int nreq = 0;
-    resource_limit *rdata = get_resourcelimit(rtype);
     int avail = 0;
 
     for (al = alist; al; al = al->next) {
         nreq += required(al->want, al->save);
     }
 
-    if (rdata->limit) {
-        avail = rdata->limit(r, rtype);
-        if (avail < 0)
+    if (!rtype->raw) {
+        avail = limit_resource(r, rtype);
+        if (avail < 0) {
             avail = 0;
+        }
     }
 
     avail = MIN(avail, nreq);
@@ -1147,10 +1143,11 @@ attrib_allocation(const resource_type * rtype, region * r, allocation * alist)
             nreq -= want;
             al->get = x * al->save.sa[0] / al->save.sa[1];
             al->get = MIN(al->want, al->get);
-            if (rdata->produce) {
+            if (!rtype->raw) {
                 int use = required(al->get, al->save);
-                if (use)
-                    rdata->produce(r, rtype, use);
+                if (use) {
+                    produce_resource(r, rtype, use);
+                }
             }
         }
     }
@@ -1162,15 +1159,10 @@ typedef void(*allocate_function) (const resource_type *, struct region *,
 
 static allocate_function get_allocator(const struct resource_type *rtype)
 {
-    resource_limit *rdata = get_resourcelimit(rtype);
-
-    if (rdata) {
-        if (rdata->limit != NULL) {
-            return attrib_allocation;
-        }
+    if (rtype->raw) {
         return leveled_allocation;
     }
-    return NULL;
+    return attrib_allocation;
 }
 
 void split_allocations(region * r)
diff --git a/src/helpers.c b/src/helpers.c
index e9502e075..ad222a0c6 100644
--- a/src/helpers.c
+++ b/src/helpers.c
@@ -27,6 +27,7 @@ without prior permission by the authors of Eressea.
 #include <kernel/faction.h>
 #include <kernel/spell.h>
 #include <kernel/race.h>
+#include <kernel/resources.h>
 #include <kernel/unit.h>
 #include <kernel/building.h>
 #include <kernel/item.h>
@@ -78,7 +79,7 @@ lua_giveitem(unit * s, unit * d, const item_type * itype, int n, struct order *o
     return result;
 }
 
-static int limit_resource(const region * r, const resource_type * rtype)
+static int limit_resource_lua(const region * r, const resource_type * rtype)
 {
     char fname[64];
     int result = -1;
@@ -110,7 +111,7 @@ static int limit_resource(const region * r, const resource_type * rtype)
 }
 
 static void
-produce_resource(region * r, const resource_type * rtype, int norders)
+produce_resource_lua(region * r, const resource_type * rtype, int norders)
 {
     lua_State *L = (lua_State *)global.vm_state;
     char fname[64];
@@ -564,9 +565,7 @@ void register_tolua_helpers(void)
     register_function((pf_generic)lua_maintenance,
         TOLUA_CAST "lua_maintenance");
 
-    register_function((pf_generic)produce_resource,
-        TOLUA_CAST "lua_produceresource");
-    register_function((pf_generic)limit_resource,
-        TOLUA_CAST "lua_limitresource");
+    res_produce_fun = produce_resource_lua;
+    res_limit_fun = limit_resource_lua;
     register_item_give(lua_giveitem, TOLUA_CAST "lua_giveitem");
 }
diff --git a/src/kernel/resources.c b/src/kernel/resources.c
index ae93d542a..f99783763 100644
--- a/src/kernel/resources.c
+++ b/src/kernel/resources.c
@@ -69,6 +69,7 @@ const resource_type * rtype)
     rm->divisor = divisor;
     rm->flags = 0;
     rm->type = rmt_get(rtype);
+    assert(rm->type);
     update_resource(rm, 1.0);
     rm->type->terraform(rm, r);
 }
@@ -211,3 +212,23 @@ struct rawmaterial_type *rmt_create(struct resource_type *rtype)
     rmtype->visible = visible_default;
     return rmtype;
 }
+
+int(*res_limit_fun)(const struct region *, const struct resource_type *);
+void(*res_produce_fun)(struct region *, const struct resource_type *, int);
+
+int limit_resource(const struct region *r, const resource_type *rtype)
+{
+    assert(!rtype->raw);
+    if (res_limit_fun) {
+        return res_limit_fun(r, rtype);
+    }
+    return -1;
+}
+
+void produce_resource(struct region *r, const struct resource_type *rtype, int amount)
+{
+    assert(!rtype->raw);
+    if (res_produce_fun) {
+        res_produce_fun(r, rtype, amount);
+    }
+}
diff --git a/src/kernel/resources.h b/src/kernel/resources.h
index d7c0e5bfd..6ee021fc1 100644
--- a/src/kernel/resources.h
+++ b/src/kernel/resources.h
@@ -43,11 +43,6 @@ extern "C" {
         struct rawmaterial *next;
     } rawmaterial;
 
-    typedef int(*rlimit_limit) (const struct region * r,
-        const struct resource_type * rtype);
-    typedef void(*rlimit_produce) (struct region * r,
-        const struct resource_type * rtype, int n);
-
     typedef struct resource_mod {
         variant value;
         const struct building_type *btype;
@@ -56,8 +51,6 @@ extern "C" {
     } resource_mod;
 
     typedef struct resource_limit {
-        rlimit_limit limit;
-        rlimit_produce produce;
         resource_mod *modifiers;
     } resource_limit;
 
@@ -83,6 +76,12 @@ extern "C" {
         const struct resource_type *rtype);
     struct rawmaterial_type *rmt_create(struct resource_type *rtype);
 
+    extern int(*res_limit_fun)(const struct region *, const struct resource_type *);
+    extern void(*res_produce_fun)(struct region *, const struct resource_type *, int);
+
+    int limit_resource(const struct region *r, const struct resource_type *rtype);
+    void produce_resource(struct region *r, const struct resource_type *rtype, int amount);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/kernel/xmlreader.c b/src/kernel/xmlreader.c
index 737a12f66..ae8a76163 100644
--- a/src/kernel/xmlreader.c
+++ b/src/kernel/xmlreader.c
@@ -933,8 +933,6 @@ static int parse_resources(xmlDocPtr doc)
 
         if (xml_bvalue(node, "pooled", true))
             flags |= RTF_POOLED;
-        if (xml_bvalue(node, "limited", false))
-            flags |= RTF_LIMITED;
 
         name = xmlGetProp(node, BAD_CAST "name");
         if (!name) {
@@ -1044,39 +1042,6 @@ static int parse_resources(xmlDocPtr doc)
                     xmlFree(propValue);
                 }
             }
-            xmlXPathFreeObject(result);
-
-            /* reading eressea/resources/resource/resourcelimit/function */
-            result = xmlXPathEvalExpression(BAD_CAST "function", xpath);
-            if (result->nodesetval != NULL) {
-                for (k = 0; k != result->nodesetval->nodeNr; ++k) {
-                    xmlNodePtr node = result->nodesetval->nodeTab[k];
-                    pf_generic fun;
-
-                    propValue = xmlGetProp(node, BAD_CAST "value");
-                    assert(propValue != NULL);
-                    fun = get_function((const char *)propValue);
-                    if (fun == NULL) {
-                        log_error("unknown limit '%s' for resource %s\n", (const char *)propValue, rtype->_name);
-                        xmlFree(propValue);
-                        continue;
-                    }
-                    xmlFree(propValue);
-
-                    propValue = xmlGetProp(node, BAD_CAST "name");
-                    assert(propValue != NULL);
-                    if (strcmp((const char *)propValue, "produce") == 0) {
-                        rdata->produce = (rlimit_produce)fun;
-                    }
-                    else if (strcmp((const char *)propValue, "limit") == 0) {
-                        rdata->limit = (rlimit_limit)fun;
-                    }
-                    else {
-                        log_error("unknown limit '%s' for resource %s\n", (const char *)propValue, rtype->_name);
-                    }
-                    xmlFree(propValue);
-                }
-            }
         }
         xmlXPathFreeObject(result);
         /* reading eressea/resources/resource/resourcelimit/function */

From c3b0b9e8b339fee47f065e67762aef0d5d0a4af9 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sun, 26 Feb 2017 13:47:22 +0100
Subject: [PATCH 02/42] fix missing limit-flags.

---
 res/core/resources/horse.xml       | 4 ----
 res/core/resources/log.xml         | 4 +---
 res/core/resources/mallorn.xml     | 4 +---
 res/core/resources/mallornseed.xml | 4 ----
 res/core/resources/seed.xml        | 4 ----
 src/economy.c                      | 3 +--
 src/kernel/xmlreader.c             | 4 +++-
 7 files changed, 6 insertions(+), 21 deletions(-)

diff --git a/res/core/resources/horse.xml b/res/core/resources/horse.xml
index d7f794491..243b547db 100644
--- a/res/core/resources/horse.xml
+++ b/res/core/resources/horse.xml
@@ -4,8 +4,4 @@
     <construction skill="training" minskill="1"/>
     <function name="give" value="givehorses"/>
   </item>
-  <resourcelimit>
-    <function name="produce" value="lua_produceresource"/>
-    <function name="limit" value="lua_limitresource"/>
-  </resourcelimit>
 </resource>
diff --git a/res/core/resources/log.xml b/res/core/resources/log.xml
index 48662878f..3a1e8db50 100644
--- a/res/core/resources/log.xml
+++ b/res/core/resources/log.xml
@@ -1,12 +1,10 @@
 <?xml version="1.0"?>
-<resource name="log">
+<resource name="log" limited="yes">
   <item weight="500" score="10">
     <construction skill="forestry" minskill="1"/>
   </item>
   <resourcelimit>
     <modifier building="sawmill" type="skill" value="1"/>
     <modifier building="sawmill" type="material" value="0.5"/>
-    <function name="produce" value="lua_produceresource"/>
-    <function name="limit" value="lua_limitresource"/>
   </resourcelimit>
 </resource>
diff --git a/res/core/resources/mallorn.xml b/res/core/resources/mallorn.xml
index 96892b802..39a0230d1 100644
--- a/res/core/resources/mallorn.xml
+++ b/res/core/resources/mallorn.xml
@@ -1,12 +1,10 @@
 <?xml version="1.0"?>
-<resource name="mallorn">
+<resource name="mallorn" limited="yes">
   <item weight="500" score="30">
     <construction skill="forestry" minskill="2"/>
   </item>
   <resourcelimit>
     <modifier building="sawmill" type="skill" value="1"/>
     <modifier building="sawmill" type="material" value="0.5"/>
-    <function name="produce" value="lua_produceresource"/>
-    <function name="limit" value="lua_limitresource"/>
   </resourcelimit>
 </resource>
diff --git a/res/core/resources/mallornseed.xml b/res/core/resources/mallornseed.xml
index 01b8a3416..7a5a0310f 100644
--- a/res/core/resources/mallornseed.xml
+++ b/res/core/resources/mallornseed.xml
@@ -3,8 +3,4 @@
   <item weight="10" score="100">
     <construction skill="herbalism" minskill="4"/>
   </item>
-  <resourcelimit>
-    <function name="produce" value="lua_produceresource"/>
-    <function name="limit" value="lua_limitresource"/>
-  </resourcelimit>
 </resource>
diff --git a/res/core/resources/seed.xml b/res/core/resources/seed.xml
index 2bda26eeb..99f5f5804 100644
--- a/res/core/resources/seed.xml
+++ b/res/core/resources/seed.xml
@@ -3,8 +3,4 @@
   <item weight="10" score="50">
     <construction skill="herbalism" minskill="3"/>
   </item>
-  <resourcelimit>
-    <function name="produce" value="lua_produceresource"/>
-    <function name="limit" value="lua_limitresource"/>
-  </resourcelimit>
 </resource>
diff --git a/src/economy.c b/src/economy.c
index b1b9afb32..f7f23eb16 100644
--- a/src/economy.c
+++ b/src/economy.c
@@ -930,7 +930,6 @@ static void allocate_resource(unit * u, const resource_type * rtype, int want)
      * Materialverbrauch hat: */
     assert(itype != NULL && (itype->construction == NULL
         || itype->construction->materials == NULL));
-    assert(rdata != NULL);
 
     if (!rtype->raw) {
         int avail = limit_resource(r, rtype);
@@ -945,7 +944,7 @@ static void allocate_resource(unit * u, const resource_type * rtype, int want)
         return;
     }
 
-    if (rdata->modifiers) {
+    if (rdata && rdata->modifiers) {
         message *msg = get_modifiers(u, rdata->modifiers, &save_mod, &skill_mod);
         if (msg) {
             ADDMSG(&u->faction->msgs, msg);
diff --git a/src/kernel/xmlreader.c b/src/kernel/xmlreader.c
index ae8a76163..d4c31001e 100644
--- a/src/kernel/xmlreader.c
+++ b/src/kernel/xmlreader.c
@@ -980,6 +980,9 @@ static int parse_resources(xmlDocPtr doc)
             rmt_create(rtype);
         }
 
+        if (xml_bvalue(node, "limited", false)) {
+            rtype->flags |= RTF_LIMITED;
+        }
         /* reading eressea/resources/resource/resourcelimit */
         xpath->node = node;
         result = xmlXPathEvalExpression(BAD_CAST "resourcelimit", xpath);
@@ -988,7 +991,6 @@ static int parse_resources(xmlDocPtr doc)
             resource_limit *rdata = rtype->limit = calloc(1, sizeof(resource_limit));
             xmlNodePtr limit = result->nodesetval->nodeTab[0];
 
-            rtype->flags |= RTF_LIMITED;
             xpath->node = limit;
             xmlXPathFreeObject(result);
 

From d4b973fea4eb2791693f052cf1aca07a4511b23a Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sun, 26 Feb 2017 13:55:19 +0100
Subject: [PATCH 03/42] remove resource_limit struct indirection.

---
 src/economy.c          |  5 ++---
 src/economy.test.c     | 18 ++++++++----------
 src/kernel/item.c      |  9 +++------
 src/kernel/item.h      |  4 ++--
 src/kernel/resources.h |  4 ----
 src/kernel/xmlreader.c | 19 +++++++++----------
 6 files changed, 24 insertions(+), 35 deletions(-)

diff --git a/src/economy.c b/src/economy.c
index f7f23eb16..ab6862aa4 100644
--- a/src/economy.c
+++ b/src/economy.c
@@ -921,7 +921,6 @@ static void allocate_resource(unit * u, const resource_type * rtype, int want)
     int dm = 0;
     allocation_list *alist;
     allocation *al;
-    resource_limit *rdata = rtype->limit;
     const resource_type *rring;
     int amount, skill, skill_mod = 0;
     variant save_mod;
@@ -944,8 +943,8 @@ static void allocate_resource(unit * u, const resource_type * rtype, int want)
         return;
     }
 
-    if (rdata && rdata->modifiers) {
-        message *msg = get_modifiers(u, rdata->modifiers, &save_mod, &skill_mod);
+    if (rtype->modifiers) {
+        message *msg = get_modifiers(u, rtype->modifiers, &save_mod, &skill_mod);
         if (msg) {
             ADDMSG(&u->faction->msgs, msg);
             return;
diff --git a/src/economy.test.c b/src/economy.test.c
index 6d9c84542..8b1b0b0fa 100644
--- a/src/economy.test.c
+++ b/src/economy.test.c
@@ -348,7 +348,6 @@ static void test_make_item(CuTest *tc) {
     struct item_type *itype;
     const struct resource_type *rt_silver;
     resource_type *rtype;
-    resource_limit *rdata;
     double d = 0.6;
 
     test_setup();
@@ -382,7 +381,6 @@ static void test_make_item(CuTest *tc) {
     itype->construction->materials = 0;
     rtype->flags |= RTF_LIMITED;
     rmt_create(rtype);
-    rdata = rtype->limit = calloc(1, sizeof(resource_limit));
     add_resource(u->region, 1, 300, 150, rtype);
     u->region->resources->amount = 300; /* there are 300 stones at level 1 */
     set_level(u, SK_ALCHEMY, 10);
@@ -392,11 +390,11 @@ static void test_make_item(CuTest *tc) {
     CuAssertIntEquals(tc, 11, get_item(u, itype));
     CuAssertIntEquals(tc, 290, u->region->resources->amount); /* used 10 stones to make 10 stones */
 
-    rdata->modifiers = calloc(2, sizeof(resource_mod));
-    rdata->modifiers[0].flags = RMF_SAVEMATERIAL;
-    rdata->modifiers[0].race = u->_race;
-    rdata->modifiers[0].value.sa[0] = (short)(0.5+100*d);
-    rdata->modifiers[0].value.sa[1] = 100;
+    rtype->modifiers = calloc(2, sizeof(resource_mod));
+    rtype->modifiers[0].flags = RMF_SAVEMATERIAL;
+    rtype->modifiers[0].race = u->_race;
+    rtype->modifiers[0].value.sa[0] = (short)(0.5+100*d);
+    rtype->modifiers[0].value.sa[1] = 100;
     make_item(u, itype, 10);
     split_allocations(u->region);
     CuAssertIntEquals(tc, 21, get_item(u, itype));
@@ -407,9 +405,9 @@ static void test_make_item(CuTest *tc) {
     CuAssertIntEquals(tc, 22, get_item(u, itype));
     CuAssertIntEquals(tc, 283, u->region->resources->amount); /* no free lunches */
 
-    rdata->modifiers[0].flags = RMF_REQUIREDBUILDING;
-    rdata->modifiers[0].race = NULL;
-    rdata->modifiers[0].btype = bt_get_or_create("mine");
+    rtype->modifiers[0].flags = RMF_REQUIREDBUILDING;
+    rtype->modifiers[0].race = NULL;
+    rtype->modifiers[0].btype = bt_get_or_create("mine");
     make_item(u, itype, 10);
     CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error104"));
 
diff --git a/src/kernel/item.c b/src/kernel/item.c
index 6e64c147f..38d2e8711 100644
--- a/src/kernel/item.c
+++ b/src/kernel/item.c
@@ -1236,15 +1236,12 @@ void free_rtype(resource_type *rtype) {
     if (rtype->wtype) {
         free_wtype(rtype->wtype);
     }
-    if (rtype->atype) {
-        free(rtype->atype);
-    }
     if (rtype->itype) {
         free_itype(rtype->itype);
     }
-    if (rtype->raw) {
-        free(rtype->raw);
-    }
+    free(rtype->atype);
+    free(rtype->modifiers);
+    free(rtype->raw);
     free(rtype->_name);
     free(rtype);
 }
diff --git a/src/kernel/item.h b/src/kernel/item.h
index c1ac38517..4351d611d 100644
--- a/src/kernel/item.h
+++ b/src/kernel/item.h
@@ -39,7 +39,7 @@ extern "C" {
     struct storage;
     struct gamedata;
     struct rawmaterial_type;
-    struct resource_limit;
+    struct resource_mod;
 
     typedef struct item {
         struct item *next;
@@ -80,7 +80,7 @@ extern "C" {
         rtype_uget uget;
         rtype_name name;
         struct rawmaterial_type *raw;
-        struct resource_limit  *limit;
+        struct resource_mod *modifiers;
         /* --- pointers --- */
         struct attrib *attribs;
         struct item_type *itype;
diff --git a/src/kernel/resources.h b/src/kernel/resources.h
index 6ee021fc1..b448425f4 100644
--- a/src/kernel/resources.h
+++ b/src/kernel/resources.h
@@ -50,10 +50,6 @@ extern "C" {
         unsigned int flags;
     } resource_mod;
 
-    typedef struct resource_limit {
-        resource_mod *modifiers;
-    } resource_limit;
-
     typedef struct rawmaterial_type {
         const struct resource_type *rtype;
 
diff --git a/src/kernel/xmlreader.c b/src/kernel/xmlreader.c
index d4c31001e..3cf8b8601 100644
--- a/src/kernel/xmlreader.c
+++ b/src/kernel/xmlreader.c
@@ -988,7 +988,6 @@ static int parse_resources(xmlDocPtr doc)
         result = xmlXPathEvalExpression(BAD_CAST "resourcelimit", xpath);
         assert(result->nodesetval->nodeNr <= 1);
         if (result->nodesetval->nodeNr != 0) {
-            resource_limit *rdata = rtype->limit = calloc(1, sizeof(resource_limit));
             xmlNodePtr limit = result->nodesetval->nodeTab[0];
 
             xpath->node = limit;
@@ -996,7 +995,7 @@ static int parse_resources(xmlDocPtr doc)
 
             result = xmlXPathEvalExpression(BAD_CAST "modifier", xpath);
             if (result->nodesetval != NULL) {
-                rdata->modifiers =
+                rtype->modifiers =
                     calloc(result->nodesetval->nodeNr + 1, sizeof(resource_mod));
                 for (k = 0; k != result->nodesetval->nodeNr; ++k) {
                     xmlNodePtr node = result->nodesetval->nodeTab[k];
@@ -1010,31 +1009,31 @@ static int parse_resources(xmlDocPtr doc)
                             rc = rc_get_or_create((const char *)propValue);
                         xmlFree(propValue);
                     }
-                    rdata->modifiers[k].race = rc;
+                    rtype->modifiers[k].race = rc;
 
                     propValue = xmlGetProp(node, BAD_CAST "building");
                     if (propValue != NULL) {
                         btype = bt_get_or_create((const char *)propValue);
                         xmlFree(propValue);
                     }
-                    rdata->modifiers[k].btype = btype;
+                    rtype->modifiers[k].btype = btype;
 
                     propValue = xmlGetProp(node, BAD_CAST "type");
                     assert(propValue != NULL);
                     if (strcmp((const char *)propValue, "skill") == 0) {
-                        rdata->modifiers[k].value.i = xml_ivalue(node, "value", 0);
-                        rdata->modifiers[k].flags = RMF_SKILL;
+                        rtype->modifiers[k].value.i = xml_ivalue(node, "value", 0);
+                        rtype->modifiers[k].flags = RMF_SKILL;
                     }
                     else if (strcmp((const char *)propValue, "material") == 0) {
-                        rdata->modifiers[k].value = xml_fraction(node, "value");
-                        rdata->modifiers[k].flags = RMF_SAVEMATERIAL;
+                        rtype->modifiers[k].value = xml_fraction(node, "value");
+                        rtype->modifiers[k].flags = RMF_SAVEMATERIAL;
                     }
                     else if (strcmp((const char *)propValue, "require") == 0) {
                         xmlChar *propBldg = xmlGetProp(node, BAD_CAST "building");
                         if (propBldg != NULL) {
                             btype = bt_get_or_create((const char *)propBldg);
-                            rdata->modifiers[k].btype = btype;
-                            rdata->modifiers[k].flags = RMF_REQUIREDBUILDING;
+                            rtype->modifiers[k].btype = btype;
+                            rtype->modifiers[k].flags = RMF_REQUIREDBUILDING;
                             xmlFree(propBldg);
                         }
                     }

From 6e27adb892c1f7ed7f4d627797fa243bd7d79840 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sun, 26 Feb 2017 14:00:20 +0100
Subject: [PATCH 04/42] remove pointless <resourcelimit/> wrapper from XML.

---
 res/adamantium.xml             |   4 +-
 res/core/resources/iron.xml    |   8 +--
 res/core/resources/laen.xml    |   8 +--
 res/core/resources/log.xml     |   8 +--
 res/core/resources/mallorn.xml |   8 +--
 res/core/resources/stone.xml   |   8 +--
 res/e3a/resources/iron.xml     |   8 +--
 res/e3a/resources/stone.xml    |   8 +--
 src/kernel/xmlreader.c         | 101 +++++++++++++++------------------
 9 files changed, 68 insertions(+), 93 deletions(-)

diff --git a/res/adamantium.xml b/res/adamantium.xml
index bb94dfc12..77e138f77 100644
--- a/res/adamantium.xml
+++ b/res/adamantium.xml
@@ -5,9 +5,7 @@
     <item weight="200" score="200">
       <construction skill="mining" minskill="8"/>
     </item>
-    <resourcelimit>
-      <modifier type="require" building="mine"/>
-    </resourcelimit>
+    <modifier type="require" building="mine"/>
   </resource>
 
   <resource name="adamantiumaxe">
diff --git a/res/core/resources/iron.xml b/res/core/resources/iron.xml
index 469840669..70d5c1080 100644
--- a/res/core/resources/iron.xml
+++ b/res/core/resources/iron.xml
@@ -1,11 +1,9 @@
 <?xml version="1.0"?>
 <resource name="iron" limited="yes" material="yes">
-  <item weight="500" score="10">
-    <construction skill="mining" minskill="1"/>
-  </item>
-  <resourcelimit>
+    <item weight="500" score="10">
+        <construction skill="mining" minskill="1"/>
+    </item>
     <modifier building="mine" type="skill" value="1"/>
     <modifier building="mine" type="material" value="0.5"/>
     <modifier race="dwarf" type="material" value="0.60"/>
-  </resourcelimit>
 </resource>
diff --git a/res/core/resources/laen.xml b/res/core/resources/laen.xml
index 30531c0fe..4099f5ca9 100644
--- a/res/core/resources/laen.xml
+++ b/res/core/resources/laen.xml
@@ -1,9 +1,7 @@
 <?xml version="1.0"?>
 <resource name="laen" limited="yes" material="yes">
-  <item weight="200" score="100">
-    <construction skill="mining" minskill="7"/>
-  </item>
-  <resourcelimit>
+    <item weight="200" score="100">
+        <construction skill="mining" minskill="7"/>
+    </item>
     <modifier type="require" building="mine"/>
-  </resourcelimit>
 </resource>
diff --git a/res/core/resources/log.xml b/res/core/resources/log.xml
index 3a1e8db50..e3710d496 100644
--- a/res/core/resources/log.xml
+++ b/res/core/resources/log.xml
@@ -1,10 +1,8 @@
 <?xml version="1.0"?>
 <resource name="log" limited="yes">
-  <item weight="500" score="10">
-    <construction skill="forestry" minskill="1"/>
-  </item>
-  <resourcelimit>
+    <item weight="500" score="10">
+        <construction skill="forestry" minskill="1"/>
+    </item>
     <modifier building="sawmill" type="skill" value="1"/>
     <modifier building="sawmill" type="material" value="0.5"/>
-  </resourcelimit>
 </resource>
diff --git a/res/core/resources/mallorn.xml b/res/core/resources/mallorn.xml
index 39a0230d1..3dff091c6 100644
--- a/res/core/resources/mallorn.xml
+++ b/res/core/resources/mallorn.xml
@@ -1,10 +1,8 @@
 <?xml version="1.0"?>
 <resource name="mallorn" limited="yes">
-  <item weight="500" score="30">
-    <construction skill="forestry" minskill="2"/>
-  </item>
-  <resourcelimit>
+    <item weight="500" score="30">
+        <construction skill="forestry" minskill="2"/>
+    </item>
     <modifier building="sawmill" type="skill" value="1"/>
     <modifier building="sawmill" type="material" value="0.5"/>
-  </resourcelimit>
 </resource>
diff --git a/res/core/resources/stone.xml b/res/core/resources/stone.xml
index 91d3aac5d..e1c5651ed 100644
--- a/res/core/resources/stone.xml
+++ b/res/core/resources/stone.xml
@@ -1,11 +1,9 @@
 <?xml version="1.0"?>
 <resource name="stone" limited="yes" material="yes">
-  <item weight="6000" score="10" big="yes">
-    <construction skill="quarrying" minskill="1"/>
-  </item>
-  <resourcelimit>
+    <item weight="6000" score="10" big="yes">
+        <construction skill="quarrying" minskill="1"/>
+    </item>
     <modifier building="quarry" type="skill" value="1"/>
     <modifier building="quarry" type="material" value="0.5"/>
     <modifier race="troll" type="material" value="0.75"/>
-  </resourcelimit>
 </resource>
diff --git a/res/e3a/resources/iron.xml b/res/e3a/resources/iron.xml
index 7ffe36f1e..01e0d8d6d 100644
--- a/res/e3a/resources/iron.xml
+++ b/res/e3a/resources/iron.xml
@@ -1,10 +1,8 @@
 <?xml version="1.0"?>
 <resource name="iron" limited="yes" material="yes">
-  <item weight="500" score="10">
-    <construction skill="mining" minskill="1"/>
-  </item>
-  <resourcelimit>
+    <item weight="500" score="10">
+        <construction skill="mining" minskill="1"/>
+    </item>
     <modifier building="mine" type="skill" value="1"/>
     <modifier building="mine" type="material" value="0.5"/>
-  </resourcelimit>
 </resource>
diff --git a/res/e3a/resources/stone.xml b/res/e3a/resources/stone.xml
index dfb039093..d8fd08573 100644
--- a/res/e3a/resources/stone.xml
+++ b/res/e3a/resources/stone.xml
@@ -1,10 +1,8 @@
 <?xml version="1.0"?>
 <resource name="stone" limited="yes" material="yes">
-  <item weight="6000" score="10" big="yes">
-    <construction skill="quarrying" minskill="1"/>
-  </item>
-  <resourcelimit>
+    <item weight="6000" score="10" big="yes">
+        <construction skill="quarrying" minskill="1"/>
+    </item>
     <modifier building="quarry" type="skill" value="1"/>
     <modifier building="quarry" type="material" value="0.5"/>
-  </resourcelimit>
 </resource>
diff --git a/src/kernel/xmlreader.c b/src/kernel/xmlreader.c
index 3cf8b8601..21c80566c 100644
--- a/src/kernel/xmlreader.c
+++ b/src/kernel/xmlreader.c
@@ -983,65 +983,56 @@ static int parse_resources(xmlDocPtr doc)
         if (xml_bvalue(node, "limited", false)) {
             rtype->flags |= RTF_LIMITED;
         }
-        /* reading eressea/resources/resource/resourcelimit */
+        /* reading eressea/resources/resource/modifier */
         xpath->node = node;
-        result = xmlXPathEvalExpression(BAD_CAST "resourcelimit", xpath);
-        assert(result->nodesetval->nodeNr <= 1);
-        if (result->nodesetval->nodeNr != 0) {
-            xmlNodePtr limit = result->nodesetval->nodeTab[0];
+        result = xmlXPathEvalExpression(BAD_CAST "modifier", xpath);
+        if (result->nodesetval != NULL && result->nodesetval->nodeNr > 0) {
+            rtype->modifiers =
+                calloc(result->nodesetval->nodeNr + 1, sizeof(resource_mod));
+            for (k = 0; k != result->nodesetval->nodeNr; ++k) {
+                xmlNodePtr node = result->nodesetval->nodeTab[k];
+                building_type *btype = NULL;
+                const race *rc = NULL;
 
-            xpath->node = limit;
-            xmlXPathFreeObject(result);
-
-            result = xmlXPathEvalExpression(BAD_CAST "modifier", xpath);
-            if (result->nodesetval != NULL) {
-                rtype->modifiers =
-                    calloc(result->nodesetval->nodeNr + 1, sizeof(resource_mod));
-                for (k = 0; k != result->nodesetval->nodeNr; ++k) {
-                    xmlNodePtr node = result->nodesetval->nodeTab[k];
-                    building_type *btype = NULL;
-                    const race *rc = NULL;
-
-                    propValue = xmlGetProp(node, BAD_CAST "race");
-                    if (propValue != NULL) {
-                        rc = rc_find((const char *)propValue);
-                        if (rc == NULL)
-                            rc = rc_get_or_create((const char *)propValue);
-                        xmlFree(propValue);
-                    }
-                    rtype->modifiers[k].race = rc;
-
-                    propValue = xmlGetProp(node, BAD_CAST "building");
-                    if (propValue != NULL) {
-                        btype = bt_get_or_create((const char *)propValue);
-                        xmlFree(propValue);
-                    }
-                    rtype->modifiers[k].btype = btype;
-
-                    propValue = xmlGetProp(node, BAD_CAST "type");
-                    assert(propValue != NULL);
-                    if (strcmp((const char *)propValue, "skill") == 0) {
-                        rtype->modifiers[k].value.i = xml_ivalue(node, "value", 0);
-                        rtype->modifiers[k].flags = RMF_SKILL;
-                    }
-                    else if (strcmp((const char *)propValue, "material") == 0) {
-                        rtype->modifiers[k].value = xml_fraction(node, "value");
-                        rtype->modifiers[k].flags = RMF_SAVEMATERIAL;
-                    }
-                    else if (strcmp((const char *)propValue, "require") == 0) {
-                        xmlChar *propBldg = xmlGetProp(node, BAD_CAST "building");
-                        if (propBldg != NULL) {
-                            btype = bt_get_or_create((const char *)propBldg);
-                            rtype->modifiers[k].btype = btype;
-                            rtype->modifiers[k].flags = RMF_REQUIREDBUILDING;
-                            xmlFree(propBldg);
-                        }
-                    }
-                    else {
-                        log_error("unknown type '%s' for resourcelimit-modifier '%s'\n", (const char *)propValue, rtype->_name);
-                    }
+                propValue = xmlGetProp(node, BAD_CAST "race");
+                if (propValue != NULL) {
+                    rc = rc_find((const char *)propValue);
+                    if (rc == NULL)
+                        rc = rc_get_or_create((const char *)propValue);
                     xmlFree(propValue);
                 }
+                rtype->modifiers[k].race = rc;
+
+                propValue = xmlGetProp(node, BAD_CAST "building");
+                if (propValue != NULL) {
+                    btype = bt_get_or_create((const char *)propValue);
+                    xmlFree(propValue);
+                }
+                rtype->modifiers[k].btype = btype;
+
+                propValue = xmlGetProp(node, BAD_CAST "type");
+                assert(propValue != NULL);
+                if (strcmp((const char *)propValue, "skill") == 0) {
+                    rtype->modifiers[k].value.i = xml_ivalue(node, "value", 0);
+                    rtype->modifiers[k].flags = RMF_SKILL;
+                }
+                else if (strcmp((const char *)propValue, "material") == 0) {
+                    rtype->modifiers[k].value = xml_fraction(node, "value");
+                    rtype->modifiers[k].flags = RMF_SAVEMATERIAL;
+                }
+                else if (strcmp((const char *)propValue, "require") == 0) {
+                    xmlChar *propBldg = xmlGetProp(node, BAD_CAST "building");
+                    if (propBldg != NULL) {
+                        btype = bt_get_or_create((const char *)propBldg);
+                        rtype->modifiers[k].btype = btype;
+                        rtype->modifiers[k].flags = RMF_REQUIREDBUILDING;
+                        xmlFree(propBldg);
+                    }
+                }
+                else {
+                    log_error("unknown type '%s' for resourcelimit-modifier '%s'\n", (const char *)propValue, rtype->_name);
+                }
+                xmlFree(propValue);
             }
         }
         xmlXPathFreeObject(result);

From 67252e29247b84155e6e2ee434462a9c88113ac8 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sun, 26 Feb 2017 15:03:47 +0100
Subject: [PATCH 05/42] add conversion program

---
 src/CMakeLists.txt | 10 ++++++++++
 src/convert.c      |  3 +++
 2 files changed, 13 insertions(+)
 create mode 100644 src/convert.c

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 43f109fed..7be3dd3b1 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -187,6 +187,15 @@ target_link_libraries(eressea
   ${INIPARSER_LIBRARIES}
 )
 
+add_executable(convert convert.c)
+target_link_libraries(convert
+  game
+  ${LUA_MATH_LIBRARY}
+  ${STORAGE_LIBRARIES}
+  ${CLIBS_LIBRARIES}
+  ${INIPARSER_LIBRARIES}
+)
+
 set(TESTS_SRC
   monsters.test.c
   names.test.c
@@ -268,6 +277,7 @@ endif(CURSES_FOUND)
 if (LIBXML2_FOUND)
 include_directories (${LIBXML2_INCLUDE_DIR})
 target_link_libraries(eressea ${LIBXML2_LIBRARIES})
+target_link_libraries(convert ${LIBXML2_LIBRARIES})
 target_link_libraries(test_eressea ${LIBXML2_LIBRARIES})
 add_definitions(-DUSE_LIBXML2)
 endif (LIBXML2_FOUND)
diff --git a/src/convert.c b/src/convert.c
new file mode 100644
index 000000000..4558ed3ab
--- /dev/null
+++ b/src/convert.c
@@ -0,0 +1,3 @@
+int main(void) {
+    return -1;
+}

From f406c476574a86657b7f679ebba398366187d465 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sun, 26 Feb 2017 15:30:58 +0100
Subject: [PATCH 06/42] use converter frm noxml branch

---
 src/convert.c             | 26 ++++++++++++++++++++++++--
 src/kernel/CMakeLists.txt |  1 +
 src/kernel/rules.c        | 12 ++++++++++++
 src/kernel/rules.h        |  5 +++++
 4 files changed, 42 insertions(+), 2 deletions(-)
 create mode 100644 src/kernel/rules.c
 create mode 100644 src/kernel/rules.h

diff --git a/src/convert.c b/src/convert.c
index 4558ed3ab..3b982e64f 100644
--- a/src/convert.c
+++ b/src/convert.c
@@ -1,3 +1,25 @@
-int main(void) {
-    return -1;
+#include <platform.h>
+
+#include <kernel/race.h>
+#include <kernel/rules.h>
+#include <kernel/xmlreader.h>
+
+#include <races/races.h>
+
+#include <util/xml.h>
+
+#include <storage.h>
+
+int main(int argc, char **argv) {
+    const char * xmlfile, *catalog;
+
+    register_races();
+    register_xmlreader();
+
+    if (argc < 3) return -1;
+    xmlfile = argv[1];
+    catalog = argv[2];
+    read_xml(xmlfile, catalog);
+    write_rules("rules.dat");
+    return 0;
 }
diff --git a/src/kernel/CMakeLists.txt b/src/kernel/CMakeLists.txt
index d3a2e8e56..b8423ae03 100644
--- a/src/kernel/CMakeLists.txt
+++ b/src/kernel/CMakeLists.txt
@@ -54,6 +54,7 @@ pool.c
 race.c
 region.c
 resources.c
+rules.c
 save.c
 ship.c
 skills.c
diff --git a/src/kernel/rules.c b/src/kernel/rules.c
new file mode 100644
index 000000000..c7499fe0c
--- /dev/null
+++ b/src/kernel/rules.c
@@ -0,0 +1,12 @@
+#include <platform.h>
+#include "rules.h"
+
+int write_rules(const char *filename) {
+    return -1;
+}
+
+int read_rules(const char *filename)
+{
+    return -1;
+}
+
diff --git a/src/kernel/rules.h b/src/kernel/rules.h
new file mode 100644
index 000000000..98f0bf240
--- /dev/null
+++ b/src/kernel/rules.h
@@ -0,0 +1,5 @@
+#pragma once
+
+int read_rules(const char *filename);
+int write_rules(const char * filename);
+

From 71e5d101a82272d2c67d35201314cf52cf292002 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sun, 26 Feb 2017 15:33:32 +0100
Subject: [PATCH 07/42] converter compiles fine, does nothing

---
 src/convert.c | 43 +++++++++++++++++++++++++++++++------------
 1 file changed, 31 insertions(+), 12 deletions(-)

diff --git a/src/convert.c b/src/convert.c
index 3b982e64f..a67c5a287 100644
--- a/src/convert.c
+++ b/src/convert.c
@@ -1,25 +1,44 @@
 #include <platform.h>
 
+
+#ifdef USE_LIBXML2
+#include <kernel/xmlreader.h>
+#include <util/xml.h>
+#endif
 #include <kernel/race.h>
 #include <kernel/rules.h>
-#include <kernel/xmlreader.h>
-
 #include <races/races.h>
 
-#include <util/xml.h>
-
 #include <storage.h>
 
+#include <string.h>
+
+static int usage(void) {
+    return -1;
+}
+
 int main(int argc, char **argv) {
-    const char * xmlfile, *catalog;
+    const char *mode;
 
     register_races();
+#ifdef USE_LIBXML2
     register_xmlreader();
-
-    if (argc < 3) return -1;
-    xmlfile = argv[1];
-    catalog = argv[2];
-    read_xml(xmlfile, catalog);
-    write_rules("rules.dat");
-    return 0;
+#endif
+    if (argc < 2) return usage();
+    mode = argv[1];
+#ifdef USE_LIBXML2
+    if (strcmp(mode, "rules")==0) {
+        const char *xmlfile, *catalog;
+        if (argc < 4) return usage();
+        xmlfile = argv[2];
+        catalog = argv[3];
+        read_xml(xmlfile, catalog);
+        write_rules("rules.dat");
+        return 0;
+    }
+#endif
+    if (strcmp(mode, "po")==0) {
+        return 0;
+    }
+    return usage();
 }

From 56eb1b753cb33f9db228644370450dc91b905517 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sun, 26 Feb 2017 15:52:58 +0100
Subject: [PATCH 08/42] fix header missing a struct

---
 src/kernel/xmlreader.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/kernel/xmlreader.h b/src/kernel/xmlreader.h
index 5dfb643f1..290b785c3 100644
--- a/src/kernel/xmlreader.h
+++ b/src/kernel/xmlreader.h
@@ -12,6 +12,9 @@ without prior permission by the authors of Eressea.
 
 #ifndef H_KRNL_XMLREADER_H
 #define H_KRNL_XMLREADER_H
+
+struct spell;
+
 #ifdef __cplusplus
 extern "C" {
 #endif

From 3c60f863a572ad5c04f2c9c0be69267a04000c78 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sun, 26 Feb 2017 18:17:58 +0100
Subject: [PATCH 09/42] remove the special_resources trie. memory leak, bad
 performance.

---
 scripts/eressea/xmasitems.lua |  1 +
 src/bind_region.c             | 93 +++++++++++++++--------------------
 2 files changed, 41 insertions(+), 53 deletions(-)

diff --git a/scripts/eressea/xmasitems.lua b/scripts/eressea/xmasitems.lua
index a05e023b8..27ea641ee 100644
--- a/scripts/eressea/xmasitems.lua
+++ b/scripts/eressea/xmasitems.lua
@@ -25,6 +25,7 @@ end
 
 function use_stardust(u, amount)
   local p = u.region:get_resource("peasant")
+  assert(p>0)
   p = math.ceil(1.5 * p)
   u.region:set_resource("peasant", p)
   local msg = usepotion_message(u, "stardust")
diff --git a/src/bind_region.c b/src/bind_region.c
index 51a5bedd4..9e4e2f778 100644
--- a/src/bind_region.c
+++ b/src/bind_region.c
@@ -341,20 +341,16 @@ static int tolua_region_get_resourcelevel(lua_State * L)
 
 #define LUA_ASSERT(c, s) if (!(c)) { log_error("%s(%d): %s\n", __FILE__, __LINE__, (s)); return 0; }
 
-static critbit_tree * special_resources(void)
-{
-    static critbit_tree cb = CRITBIT_TREE();
-    if (!cb.root) {
-        const char * special[] = { "seed", "sapling", "tree", "grave", "chaos", 0 };
-        char buffer[32];
-        int i;
-        for (i = 0; special[i]; ++i) {
-            size_t len = strlen(special[i]);
-            len = cb_new_kv(special[i], len, &i, sizeof(int), buffer);
-            cb_insert(&cb, buffer, len);
+static int special_resource(const char *type) {
+    const char * special[] = { "seed", "sapling", "tree", "grave", "chaos", 0 };
+    int i;
+
+    for (i = 0; special[i]; ++i) {
+        if (strcmp(type, special[i]) == 0) {
+            return i;
         }
     }
-    return &cb;
+    return -1;
 }
 
 static int tolua_region_get_resource(lua_State * L)
@@ -362,32 +358,27 @@ static int tolua_region_get_resource(lua_State * L)
     region *r;
     const char *type;
     const resource_type *rtype;
-    int result = 0;
-    void * match;
-    critbit_tree * cb = special_resources();
+    int result;
 
     r = (region *)tolua_tousertype(L, 1, 0);
     LUA_ASSERT(r != NULL, "invalid parameter");
     type = tolua_tostring(L, 2, 0);
     LUA_ASSERT(type != NULL, "invalid parameter");
-
-    if (cb_find_prefix(cb, type, strlen(type) + 1, &match, 1, 0)) {
-        cb_get_kv(match, &result, sizeof(result));
-        switch (result) {
-        case 0:
-        case 1:
-        case 2:
-            result = rtrees(r, result);
-            break;
-        case 3:
-            result = deathcount(r);
-            break;
-        case 4:
-            result = get_chaoscount(r);
-            break;
-        }
-    }
-    else {
+    
+    result = special_resource(type);
+    switch (result) {
+    case 0:
+    case 1:
+    case 2:
+        result = rtrees(r, result);
+        break;
+    case 3:
+        result = deathcount(r);
+        break;
+    case 4:
+        result = get_chaoscount(r);
+        break;
+    default:
         rtype = rt_find(type);
         if (rtype) {
             result = region_getresource(r, rtype);
@@ -406,27 +397,23 @@ static int tolua_region_set_resource(lua_State * L)
     region *r = (region *)tolua_tousertype(L, 1, 0);
     const char *type = tolua_tostring(L, 2, 0);
     int result, value = (int)tolua_tonumber(L, 3, 0);
-    critbit_tree * cb = special_resources();
-    void * match;
+    const resource_type *rtype;
 
-    if (cb_find_prefix(cb, type, strlen(type) + 1, &match, 1, 0)) {
-        cb_get_kv(match, &result, sizeof(result));
-        switch (result) {
-        case 0:
-        case 1:
-        case 2:
-            rsettrees(r, result, value);
-            break;
-        case 3:
-            deathcounts(r, value - deathcount(r));
-            break;
-        case 4:
-            add_chaoscount(r, value - get_chaoscount(r));
-            break;
-        }
-    }
-    else {
-        const resource_type *rtype = rt_find(type);
+    result = special_resource(type);
+    switch (result) {
+    case 0:
+    case 1:
+    case 2:
+        rsettrees(r, result, value);
+        break;
+    case 3:
+        deathcounts(r, value - deathcount(r));
+        break;
+    case 4:
+        add_chaoscount(r, value - get_chaoscount(r));
+        break;
+    default:
+        rtype = rt_find(type);
         if (rtype != NULL) {
             region_setresource(r, rtype, value);
         }

From b8ffc20d87c4b592e4c8c8cf43a21b2abec840f6 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno@eressea.de>
Date: Sun, 26 Feb 2017 19:54:58 +0100
Subject: [PATCH 10/42] remove item_useonother callbacks (use is fine). add a
 test for foolpotion.

---
 res/core/common/potions.xml     |  2 +-
 scripts/tests/e2/e2features.lua | 24 ++++++++++++++++++++++++
 src/kernel/item.c               | 13 ++++++-------
 src/kernel/item.h               |  2 --
 src/kernel/xmlreader.c          |  5 -----
 src/laws.c                      | 22 ++++++----------------
 6 files changed, 37 insertions(+), 31 deletions(-)

diff --git a/res/core/common/potions.xml b/res/core/common/potions.xml
index 84b1e5f52..12ac67aa2 100644
--- a/res/core/common/potions.xml
+++ b/res/core/common/potions.xml
@@ -101,7 +101,7 @@
 
   <resource name="p7" appearance="vial">
     <item weight="0" score="90">
-      <function name="useonother" value="usefoolpotion"/>
+      <function name="use" value="usefoolpotion"/>
       <potion level="3"/>
       <construction skill="alchemy" minskill="6">
         <requirement type="h2"/>
diff --git a/scripts/tests/e2/e2features.lua b/scripts/tests/e2/e2features.lua
index de94ea34f..28cbbf69b 100644
--- a/scripts/tests/e2/e2features.lua
+++ b/scripts/tests/e2/e2features.lua
@@ -230,6 +230,30 @@ function test_no_uruk()
   assert_equal(f1.race, "orc")
 end
 
+function test_foolpotion()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u:add_item("p7", 1)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Dumpfbackenbrot 4242")
+    process_orders()
+    assert_equal(1, u:get_item("p7"))
+    assert_equal(1, f:count_msg_type('feedback_unit_not_found'))
+    local u2 = unit.create(f, r, 1)
+    
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Dumpfbackenbrot " .. itoa36(u2.id))
+    process_orders()
+    assert_equal(1, u:get_item("p7"))
+    assert_equal(1, f:count_msg_type('error64'))
+
+    u:set_skill("stealth", 1);
+    process_orders()
+    assert_equal(0, u:get_item("p7"))
+    assert_equal(1, f:count_msg_type('givedumb'))
+end
+
 function test_snowman()
     local r = region.create(0, 0, "glacier")
     local f = faction.create("noreply@eressea.de", "human", "de")
diff --git a/src/kernel/item.c b/src/kernel/item.c
index 38d2e8711..9240a7994 100644
--- a/src/kernel/item.c
+++ b/src/kernel/item.c
@@ -820,9 +820,8 @@ struct order *ord)
     return 0;
 }
 
-static int
-use_warmthpotion(struct unit *u, const struct item_type *itype, int amount,
-struct order *ord)
+static int use_warmthpotion(unit *u, const item_type *itype,
+    int amount, struct order *ord)
 {
     if (u->faction->race == get_race(RC_INSECT)) {
         fset(u, UFL_WARMTH);
@@ -841,10 +840,10 @@ struct order *ord)
     return 0;
 }
 
-static int
-use_foolpotion(struct unit *u, int targetno, const struct item_type *itype,
-int amount, struct order *ord)
+static int use_foolpotion(unit *u, const item_type *itype, int amount,
+    struct order *ord)
 {
+    int targetno = read_unitid(u->faction, u->region);
     unit *target = findunit(targetno);
     if (target == NULL || u->region != target->region) {
         ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found",
@@ -1295,7 +1294,7 @@ void register_resources(void)
     register_item_use(use_warmthpotion, "usewarmthpotion");
     register_item_use(use_bloodpotion, "usebloodpotion");
     register_item_use(use_healingpotion, "usehealingpotion");
-    register_item_useonother(use_foolpotion, "usefoolpotion");
+    register_item_use(use_foolpotion, "usefoolpotion");
     register_item_use(use_mistletoe, "usemistletoe");
     register_item_use(use_magicboost, "usemagicboost");
     register_item_use(use_snowball, "usesnowball");
diff --git a/src/kernel/item.h b/src/kernel/item.h
index 4351d611d..a2e588318 100644
--- a/src/kernel/item.h
+++ b/src/kernel/item.h
@@ -127,8 +127,6 @@ extern "C" {
             const struct item_type * itype);
         int(*use) (struct unit * user, const struct item_type * itype, int amount,
         struct order * ord);
-        int(*useonother) (struct unit * user, int targetno,
-            const struct item_type * itype, int amount, struct order * ord);
         int(*give) (struct unit * src, struct unit * dest,
             const struct item_type * itm, int number, struct order * ord);
         int score;
diff --git a/src/kernel/xmlreader.c b/src/kernel/xmlreader.c
index 21c80566c..f5e45dd5c 100644
--- a/src/kernel/xmlreader.c
+++ b/src/kernel/xmlreader.c
@@ -857,11 +857,6 @@ static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype)
             itype->canuse =
                 (bool(*)(const struct unit *, const struct item_type *))fun;
         }
-        else if (strcmp((const char *)propValue, "useonother") == 0) {
-            itype->useonother =
-                (int(*)(struct unit *, int, const struct item_type *, int,
-            struct order *))fun;
-        }
         else {
             log_error("unknown function type '%s' for item '%s'\n", (const char *)propValue, rtype->_name);
         }
diff --git a/src/laws.c b/src/laws.c
index 253b45b89..09b1439c0 100644
--- a/src/laws.c
+++ b/src/laws.c
@@ -3242,11 +3242,6 @@ void update_long_order(unit * u)
 static int use_item(unit * u, const item_type * itype, int amount, struct order *ord)
 {
     int i;
-    int target = -1;
-
-    if (itype->useonother) {
-        target = read_unitid(u->faction, u->region);
-    }
 
     i = get_pooled(u, itype->rtype, GET_DEFAULT, amount);
     if (amount > i) {
@@ -3257,19 +3252,14 @@ static int use_item(unit * u, const item_type * itype, int amount, struct order
         return ENOITEM;
     }
 
-    if (target == -1) {
-        if (itype->use) {
-            int result = itype->use(u, itype, amount, ord);
-            if (result > 0) {
-                use_pooled(u, itype->rtype, GET_DEFAULT, result);
-            }
-            return result;
+    if (itype->use) {
+        int result = itype->use(u, itype, amount, ord);
+        if (result > 0) {
+            use_pooled(u, itype->rtype, GET_DEFAULT, result);
         }
-        return EUNUSABLE;
-    }
-    else {
-        return itype->useonother(u, target, itype, amount, ord);
+        return result;
     }
+    return EUNUSABLE;
 }
 
 void monthly_healing(void)

From 8b69b6d003358e88b278f717bfb2e22d91355b0b Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno@eressea.de>
Date: Mon, 27 Feb 2017 04:00:45 +0100
Subject: [PATCH 11/42] test peasantblood effects

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

diff --git a/scripts/tests/e2/e2features.lua b/scripts/tests/e2/e2features.lua
index 28cbbf69b..eb2d06f0d 100644
--- a/scripts/tests/e2/e2features.lua
+++ b/scripts/tests/e2/e2features.lua
@@ -230,6 +230,32 @@ function test_no_uruk()
   assert_equal(f1.race, "orc")
 end
 
+function test_bloodpotion_demon()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "demon", "de")
+    local u = unit.create(f, r, 1)
+    u:add_item("peasantblood", 1)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Bauernblut")
+    process_orders()
+    assert_equal(0, u:get_item("peasantblood"))
+    assert_equal(1, f:count_msg_type('usepotion'))
+    assert_equal("demon", u.race)
+end
+
+function test_bloodpotion_other()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u:add_item("peasantblood", 1)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Bauernblut")
+    process_orders()
+    assert_equal(0, u:get_item("peasantblood"))
+    assert_equal(1, f:count_msg_type('usepotion'))
+    assert_equal("smurf", u.race)
+end
+
 function test_foolpotion()
     local r = region.create(0, 0, "plain")
     local f = faction.create("noreply@eressea.de", "human", "de")

From 44c3838d790c290bff577e385be65d3f2f8182b9 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno@eressea.de>
Date: Mon, 27 Feb 2017 04:22:28 +0100
Subject: [PATCH 12/42] WIP: itype->use elimination.

---
 res/e3a/items.xml      |  4 +---
 res/eressea/items.xml  | 24 ++++++------------------
 src/helpers.c          | 10 +++++++---
 src/kernel/item.h      |  1 +
 src/kernel/resources.c |  2 ++
 src/kernel/resources.h |  3 ++-
 src/kernel/xmlreader.c |  3 +++
 src/laws.c             |  4 ++--
 8 files changed, 24 insertions(+), 27 deletions(-)

diff --git a/res/e3a/items.xml b/res/e3a/items.xml
index 7d7e6b286..fa3af4b41 100644
--- a/res/e3a/items.xml
+++ b/res/e3a/items.xml
@@ -81,9 +81,7 @@
   </resource>
 
   <resource name="xmastree">
-    <item weight="0">
-      <function name="use" value="lua_useitem"/>
-    </item>
+    <item weight="0" use="yes" />
   </resource>
 
 </resources>
diff --git a/res/eressea/items.xml b/res/eressea/items.xml
index 69f84c890..c0b8b5126 100644
--- a/res/eressea/items.xml
+++ b/res/eressea/items.xml
@@ -17,21 +17,15 @@
   </resource>
 
   <resource name="snowman">
-    <item notlost="yes" weight="1">
-      <function name="use" value="lua_useitem"/>
-    </item>
+    <item notlost="yes" weight="1" use="yes" />
   </resource>
 
   <resource name="snowglobe">
-    <item notlost="yes" weight="1">
-      <function name="use" value="lua_useitem"/>
-    </item>
+    <item notlost="yes" weight="1" use="yes" />
   </resource>
 
   <resource name="ring_of_levitation" appearance="ring">
-    <item notlost="yes" weight="0" cursed="true">
-      <function name="use" value="lua_useitem"/>
-    </item>
+    <item notlost="yes" weight="0" cursed="true" use="yes" />
   </resource>
 
   <resource name="birthdaycake">
@@ -44,23 +38,17 @@
 
 <!-- ambassador rewards -->
   <resource name="seashell">
-    <item cursed="true" weight="0">
-      <function name="use" value="lua_useitem"/>
-    </item>
+    <item cursed="true" weight="0" use="yes" />
   </resource>
 
 <!-- xmas 2005 -->
   <resource name="stardust" appearance="vial">
-    <item weight="0">
-      <function name="use" value="lua_useitem"/>
-    </item>
+    <item weight="0" use="yes" />
   </resource>
 
 <!-- xmas 2006 -->
   <resource name="xmastree">
-    <item weight="0">
-      <function name="use" value="lua_useitem"/>
-    </item>
+    <item weight="0" use="yes" />
   </resource>
 
 <!-- museum items -->
diff --git a/src/helpers.c b/src/helpers.c
index ad222a0c6..e5bdf6ff9 100644
--- a/src/helpers.c
+++ b/src/helpers.c
@@ -490,14 +490,18 @@ static int lua_equipmentcallback(const struct equipment *eq, unit * u)
 }
 
 /** callback for an item-use function written in lua. */
-int
-lua_useitem(struct unit *u, const struct item_type *itype, int amount,
+static int
+use_item_lua(struct unit *u, const struct item_type *itype, int amount,
 struct order *ord)
 {
     lua_State *L = (lua_State *)global.vm_state;
     int result = 0;
     char fname[64];
 
+    if (itype->use) {
+        return itype->use(u, itype, amount, ord);
+    }
+
     strlcpy(fname, "use_", sizeof(fname));
     strlcat(fname, itype->rtype->_name, sizeof(fname));
 
@@ -551,7 +555,6 @@ void register_tolua_helpers(void)
     register_function((pf_generic)lua_callspell, TOLUA_CAST "lua_castspell");
     register_function((pf_generic)lua_initfamiliar,
         TOLUA_CAST "lua_initfamiliar");
-    register_item_use(&lua_useitem, TOLUA_CAST "lua_useitem");
     register_function((pf_generic)lua_getresource,
         TOLUA_CAST "lua_getresource");
     register_function((pf_generic)lua_canuse_item,
@@ -565,6 +568,7 @@ void register_tolua_helpers(void)
     register_function((pf_generic)lua_maintenance,
         TOLUA_CAST "lua_maintenance");
 
+    item_use_fun = use_item_lua;
     res_produce_fun = produce_resource_lua;
     res_limit_fun = limit_resource_lua;
     register_item_give(lua_giveitem, TOLUA_CAST "lua_giveitem");
diff --git a/src/kernel/item.h b/src/kernel/item.h
index a2e588318..b0bc10cd8 100644
--- a/src/kernel/item.h
+++ b/src/kernel/item.h
@@ -107,6 +107,7 @@ extern "C" {
 #define ITF_BIG              0x0008     /* big item, e.g. does not fit in a bag of holding */
 #define ITF_ANIMAL           0x0010     /* an animal */
 #define ITF_VEHICLE          0x0020     /* a vehicle, drawn by two animals */
+#define ITF_CANUSE           0x0040     /* can be used with use_item_fun callout */
 
     /* error codes for item_type::use */
 #define ECUSTOM   -1
diff --git a/src/kernel/resources.c b/src/kernel/resources.c
index f99783763..10cb2bab6 100644
--- a/src/kernel/resources.c
+++ b/src/kernel/resources.c
@@ -213,6 +213,8 @@ struct rawmaterial_type *rmt_create(struct resource_type *rtype)
     return rmtype;
 }
 
+int(*item_use_fun)(struct unit *u, const struct item_type *itype, int amount,
+    struct order *ord);
 int(*res_limit_fun)(const struct region *, const struct resource_type *);
 void(*res_produce_fun)(struct region *, const struct resource_type *, int);
 
diff --git a/src/kernel/resources.h b/src/kernel/resources.h
index b448425f4..7f48fa0dd 100644
--- a/src/kernel/resources.h
+++ b/src/kernel/resources.h
@@ -74,7 +74,8 @@ extern "C" {
 
     extern int(*res_limit_fun)(const struct region *, const struct resource_type *);
     extern void(*res_produce_fun)(struct region *, const struct resource_type *, int);
-
+    extern int (*item_use_fun)(struct unit *, const struct item_type *, int amount,
+        struct order *ord);
     int limit_resource(const struct region *r, const struct resource_type *rtype);
     void produce_resource(struct region *r, const struct resource_type *rtype, int amount);
 
diff --git a/src/kernel/xmlreader.c b/src/kernel/xmlreader.c
index f5e45dd5c..6f62738e3 100644
--- a/src/kernel/xmlreader.c
+++ b/src/kernel/xmlreader.c
@@ -767,6 +767,8 @@ static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype)
 
     if (xml_bvalue(node, "cursed", false))
         flags |= ITF_CURSED;
+    if (xml_bvalue(node, "use", false))
+        flags |= ITF_CANUSE;
     if (xml_bvalue(node, "notlost", false))
         flags |= ITF_NOTLOST;
     if (xml_bvalue(node, "herb", false))
@@ -849,6 +851,7 @@ static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype)
             struct order *))fun;
         }
         else if (strcmp((const char *)propValue, "use") == 0) {
+            itype->flags |= ITF_CANUSE;
             itype->use =
                 (int(*)(struct unit *, const struct item_type *, int,
             struct order *))fun;
diff --git a/src/laws.c b/src/laws.c
index 09b1439c0..0d0e53110 100644
--- a/src/laws.c
+++ b/src/laws.c
@@ -3252,8 +3252,8 @@ static int use_item(unit * u, const item_type * itype, int amount, struct order
         return ENOITEM;
     }
 
-    if (itype->use) {
-        int result = itype->use(u, itype, amount, ord);
+    if (itype->flags & ITF_CANUSE) {
+        int result = item_use_fun(u, itype, amount, ord);
         if (result > 0) {
             use_pooled(u, itype->rtype, GET_DEFAULT, result);
         }

From 715c8569ba50b3f53d4824122244de34bf130a37 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno@eressea.de>
Date: Mon, 27 Feb 2017 09:48:24 +0100
Subject: [PATCH 13/42] hacked the item-use function,        XML needs cleaning
 up, funpointer needs to die

---
 src/helpers.c          | 27 ++++++++++++++++++---------
 src/kernel/item.c      |  4 ++--
 src/kernel/xmlreader.c |  4 ++++
 3 files changed, 24 insertions(+), 11 deletions(-)

diff --git a/src/helpers.c b/src/helpers.c
index e5bdf6ff9..5124ca0ae 100644
--- a/src/helpers.c
+++ b/src/helpers.c
@@ -13,6 +13,7 @@ without prior permission by the authors of Eressea.
 #include <platform.h>
 #include "helpers.h"
 #include "vortex.h"
+#include "alchemy.h"
 
 #include <util/attrib.h>
 #include <util/base36.h>
@@ -491,20 +492,21 @@ static int lua_equipmentcallback(const struct equipment *eq, unit * u)
 
 /** callback for an item-use function written in lua. */
 static int
-use_item_lua(struct unit *u, const struct item_type *itype, int amount,
-struct order *ord)
+use_item_lua(unit *u, const item_type *itype, int amount, struct order *ord)
 {
     lua_State *L = (lua_State *)global.vm_state;
     int result = 0;
     char fname[64];
-
-    if (itype->use) {
-        return itype->use(u, itype, amount, ord);
-    }
+    int (*callout)(unit *, const item_type *, int, struct order *);
 
     strlcpy(fname, "use_", sizeof(fname));
     strlcat(fname, itype->rtype->_name, 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)) {
         tolua_pushusertype(L, (void *)u, TOLUA_CAST "unit");
@@ -520,11 +522,18 @@ struct order *ord)
             result = (int)lua_tonumber(L, -1);
             lua_pop(L, 1);
         }
+        return result;
     }
-    else {
-        log_error("use(%s) calling '%s': not a function.\n", unitname(u), fname);
-        lua_pop(L, 1);
+    if (itype->rtype->ptype) {
+        return use_potion(u, itype, amount, ord);
+    } else {
+        log_error("no such callout: %s", fname);
     }
+    if (itype->use) {
+        return itype->use(u, itype, amount, ord);
+    }
+    log_error("use(%s) calling '%s': not a function.\n", unitname(u), fname);
+    lua_pop(L, 1);
 
     return result;
 }
diff --git a/src/kernel/item.c b/src/kernel/item.c
index 9240a7994..5991eca96 100644
--- a/src/kernel/item.c
+++ b/src/kernel/item.c
@@ -1292,9 +1292,9 @@ void register_resources(void)
     register_item_use(use_tacticcrystal, "use_tacticcrystal");
     register_item_use(use_birthdayamulet, "use_birthdayamulet");
     register_item_use(use_warmthpotion, "usewarmthpotion");
-    register_item_use(use_bloodpotion, "usebloodpotion");
+    register_item_use(use_bloodpotion, "use_peasantblood");
     register_item_use(use_healingpotion, "usehealingpotion");
-    register_item_use(use_foolpotion, "usefoolpotion");
+    register_item_use(use_foolpotion, "use_p7");
     register_item_use(use_mistletoe, "usemistletoe");
     register_item_use(use_magicboost, "usemagicboost");
     register_item_use(use_snowball, "usesnowball");
diff --git a/src/kernel/xmlreader.c b/src/kernel/xmlreader.c
index 6f62738e3..c4ce845a5 100644
--- a/src/kernel/xmlreader.c
+++ b/src/kernel/xmlreader.c
@@ -805,6 +805,10 @@ static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype)
     result = xmlXPathEvalExpression(BAD_CAST "potion", xpath);
     assert(result->nodesetval->nodeNr <= 1);
     if (result->nodesetval->nodeNr != 0) {
+        if ((itype->flags & ITF_CANUSE) == 0) {
+            log_error("potion %s has no use attribute", rtype->_name);
+            itype->flags |= ITF_CANUSE;
+        }
         xpath->node = result->nodesetval->nodeTab[0];
         rtype->ptype = xml_readpotion(xpath, itype);
     }

From 604b574d0f25e600ea8b25ac520e567adcfab55f Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno@eressea.de>
Date: Mon, 27 Feb 2017 11:39:55 +0100
Subject: [PATCH 14/42] fix potion-use? need more testing

---
 res/core/common/potions.xml     | 45 +++++++++++----------------------
 res/e3a/items.xml               | 24 ++++++------------
 scripts/tests/e2/e2features.lua | 15 +++++++++++
 src/kernel/item.c               |  7 +++--
 4 files changed, 41 insertions(+), 50 deletions(-)

diff --git a/res/core/common/potions.xml b/res/core/common/potions.xml
index 12ac67aa2..4812727dd 100644
--- a/res/core/common/potions.xml
+++ b/res/core/common/potions.xml
@@ -4,8 +4,7 @@
 
   <!-- potions -->
   <resource name="p0" appearance="vial">
-    <item weight="0" score="30">
-      <function name="use" value="usepotion"/>
+    <item weight="0" score="30" use="yes">
       <potion level="1"/>
       <construction skill="alchemy" minskill="2">
         <requirement type="h4"/>
@@ -15,8 +14,7 @@
   </resource>
 
   <resource name="goliathwater" appearance="vial">
-    <item weight="0" score="30">
-      <function name="use" value="usepotion"/>
+    <item weight="0" score="30" use="yes">
       <potion level="1"/>
       <construction skill="alchemy" minskill="2">
         <requirement type="h6"/>
@@ -26,8 +24,7 @@
   </resource>
 
   <resource name="truthpotion" appearance="vial">
-    <item weight="0" score="30">
-      <function name="use" value="usepotion"/>
+    <item weight="0" score="30" use="yes">
       <potion level="1"/>
       <construction skill="alchemy" minskill="2">
         <requirement type="h0"/>
@@ -37,8 +34,7 @@
   </resource>
 
   <resource name="p2" appearance="vial">
-    <item weight="0" score="30">
-      <function name="use" value="usepotion"/>
+    <item weight="0" score="30" use="yes">
       <potion level="1"/>
       <construction skill="alchemy" minskill="2">
         <requirement type="h5"/>
@@ -48,8 +44,7 @@
   </resource>
 
   <resource name="p3" appearance="vial">
-    <item weight="0" score="60">
-      <function name="use" value="usepotion"/>
+    <item weight="0" score="60" use="yes">
       <potion level="2"/>
       <construction skill="alchemy" minskill="4">
         <requirement type="h14"/>
@@ -61,8 +56,7 @@
 
   <resource name="ointment" appearance="vial">
     <!-- Wundsalbe -->
-    <item weight="0" score="60">
-      <function name="use" value="usehealingpotion"/>
+    <item weight="0" score="60" use="yes">
       <potion level="2"/>
       <construction skill="alchemy" minskill="4">
         <requirement type="h19"/>
@@ -74,8 +68,7 @@
 
   <resource name="peasantblood" appearance="vial">
     <!-- Bauernblut -->
-    <item weight="0" score="60">
-      <function name="use" value="usebloodpotion"/>
+    <item weight="0" score="60" use="yes">
       <potion level="2"/>
       <construction skill="alchemy" minskill="4">
         <requirement type="h17"/>
@@ -87,8 +80,7 @@
   </resource>
 
   <resource name="p6" appearance="vial">
-    <item weight="0" score="90">
-      <function name="use" value="usepotion"/>
+    <item weight="0" score="90" use="yes">
       <potion level="3"/>
       <construction skill="alchemy" minskill="6">
         <requirement type="h9"/>
@@ -100,8 +92,7 @@
   </resource>
 
   <resource name="p7" appearance="vial">
-    <item weight="0" score="90">
-      <function name="use" value="usefoolpotion"/>
+    <item weight="0" score="90" use="yes">
       <potion level="3"/>
       <construction skill="alchemy" minskill="6">
         <requirement type="h2"/>
@@ -113,8 +104,7 @@
   </resource>
 
   <resource name="nestwarmth" appearance="vial">
-    <item weight="0" score="90">
-      <function name="use" value="usewarmthpotion"/>
+    <item weight="0" score="90" use="yes">
       <potion level="3"/>
       <construction skill="alchemy" minskill="6">
         <requirement type="h18"/>
@@ -126,8 +116,7 @@
   </resource>
 
   <resource name="p9" appearance="vial">
-    <item weight="0" score="90">
-      <function name="use" value="usepotion"/>
+    <item weight="0" score="90" use="yes">
       <potion level="3"/>
       <construction skill="alchemy" minskill="6">
         <requirement type="h4"/>
@@ -139,8 +128,7 @@
   </resource>
 
   <resource name="p10" appearance="vial">
-    <item weight="0" score="90">
-      <function name="use" value="usepotion"/>
+    <item weight="0" score="90" use="yes">
       <potion level="3"/>
       <construction skill="alchemy" minskill="6">
         <requirement type="h19"/>
@@ -152,8 +140,7 @@
   </resource>
 
   <resource name="p11" appearance="vial">
-    <item weight="0" score="120">
-      <function name="use" value="usepotion"/>
+    <item weight="0" score="120" use="yes">
       <potion level="4"/>
       <construction skill="alchemy" minskill="8">
         <requirement type="h14"/>
@@ -166,8 +153,7 @@
   </resource>
 
   <resource name="p13" appearance="vial">
-    <item weight="0" score="120">
-      <function name="use" value="usepotion"/>
+    <item weight="0" score="120" use="yes">
       <potion level="4"/>
       <construction skill="alchemy" minskill="8">
         <requirement type="h5"/>
@@ -181,8 +167,7 @@
   </resource>
 
   <resource name="p14" appearance="vial">
-    <item weight="0" score="120">
-      <function name="use" value="usepotion"/>
+    <item weight="0" score="120" use="yes">
       <potion level="4"/>
       <construction skill="alchemy" minskill="8">
         <requirement type="h6"/>
diff --git a/res/e3a/items.xml b/res/e3a/items.xml
index fa3af4b41..9e928837e 100644
--- a/res/e3a/items.xml
+++ b/res/e3a/items.xml
@@ -7,64 +7,56 @@
   
   <resource name="ointment" appearance="vial">
     <!-- Wundsalbe -->
-    <item weight="0" score="60">
-      <function name="use" value="usehealingpotion"/>
+    <item weight="0" score="60" use="yes">
       <potion level="2"/>
     </item>
   </resource>
 
   <resource name="p13" appearance="vial">
     <!-- Elixier der Macht -->
-    <item weight="0" score="120">
-      <function name="use" value="usepotion"/>
+    <item weight="0" score="120" use="yes">
       <potion level="4"/>
     </item>
   </resource>
 
   <resource name="p3" appearance="vial">
     <!-- Schaffenstrunk -->
-    <item weight="0" score="60">
-      <function name="use" value="usepotion"/>
+    <item weight="0" score="60" use ="yes">
       <potion level="2"/>
     </item>
   </resource>
 
   <resource name="p14" appearance="vial">
     <!-- Heiltrank -->
-    <item weight="0" score="120">
-      <function name="use" value="usepotion"/>
+    <item weight="0" score="120" use="yes">
       <potion level="4"/>
     </item>
   </resource>
 
   <resource name="p0" appearance="vial">
     <!-- Siebenmeilentee -->
-    <item weight="0" score="30">
-      <function name="use" value="usepotion"/>
+    <item weight="0" score="30" use="yes">
       <potion level="1"/>
     </item>
   </resource>
 
   <resource name="p2" appearance="vial">
     <!-- Wasser des Lebens -->
-    <item weight="0" score="30">
-      <function name="use" value="usepotion_delayed"/>
+    <item weight="0" score="30" use="yes">
       <potion level="1"/>
     </item>
   </resource>
 
   <resource name="peasantblood" appearance="vial">
     <!-- Bauernblut -->
-    <item weight="0" score="60">
-      <function name="use" value="usebloodpotion"/>
+    <item weight="0" score="60" use="yes">
       <potion level="2"/>
     </item>
   </resource>
 
   <resource name="p9" appearance="vial">
     <!-- Pferdeglück -->
-    <item weight="0" score="90">
-      <function name="use" value="usepotion"/>
+    <item weight="0" score="90" use="yes">
       <potion level="3"/>
     </item>
   </resource>
diff --git a/scripts/tests/e2/e2features.lua b/scripts/tests/e2/e2features.lua
index eb2d06f0d..c88dfdf22 100644
--- a/scripts/tests/e2/e2features.lua
+++ b/scripts/tests/e2/e2features.lua
@@ -230,6 +230,21 @@ function test_no_uruk()
   assert_equal(f1.race, "orc")
 end
 
+function test_ointment()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    local hp = u.hp
+    u.hp = 0
+    u:add_item("ointment", 1)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Wundsalbe")
+    process_orders()
+    assert_equal(0, u:get_item("ointment"))
+    assert_equal(1, f:count_msg_type('usepotion'))
+    assert_equal(hp, u.hp)
+end
+
 function test_bloodpotion_demon()
     local r = region.create(0, 0, "plain")
     local f = faction.create("noreply@eressea.de", "demon", "de")
diff --git a/src/kernel/item.c b/src/kernel/item.c
index 5991eca96..1fa006c73 100644
--- a/src/kernel/item.c
+++ b/src/kernel/item.c
@@ -1287,13 +1287,12 @@ void register_resources(void)
     register_function((pf_generic)res_changehp, "changehp");
     register_function((pf_generic)res_changeaura, "changeaura");
 
-    register_item_use(use_potion, "usepotion");
-    register_item_use(use_potion_delayed, "usepotion_delayed");
+    register_item_use(use_potion_delayed, "use_p2");
     register_item_use(use_tacticcrystal, "use_tacticcrystal");
     register_item_use(use_birthdayamulet, "use_birthdayamulet");
-    register_item_use(use_warmthpotion, "usewarmthpotion");
+    register_item_use(use_warmthpotion, "use_nestwarmth");
     register_item_use(use_bloodpotion, "use_peasantblood");
-    register_item_use(use_healingpotion, "usehealingpotion");
+    register_item_use(use_healingpotion, "use_ointment");
     register_item_use(use_foolpotion, "use_p7");
     register_item_use(use_mistletoe, "usemistletoe");
     register_item_use(use_magicboost, "usemagicboost");

From 60c2f1e8074a94f34325d4436c2aa4088045b974 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno@eressea.de>
Date: Mon, 27 Feb 2017 14:01:41 +0100
Subject: [PATCH 15/42] test the birthday amulet

---
 res/core/common/items.xml       |  4 +---
 scripts/tests/e2/e2features.lua | 14 +++++++++++++-
 src/bind_region.c               | 20 ++++++++++++++++++++
 src/kernel/item.c               |  2 +-
 src/kernel/xmlreader.c          |  1 +
 5 files changed, 36 insertions(+), 5 deletions(-)

diff --git a/res/core/common/items.xml b/res/core/common/items.xml
index c903a6b78..8745b6e59 100644
--- a/res/core/common/items.xml
+++ b/res/core/common/items.xml
@@ -62,9 +62,7 @@
   </resource>
 
   <resource name="aoc" appearance="amulet">
-    <item weight="100">
-      <function name="use" value="use_birthdayamulet"/>
-    </item>
+    <item weight="100" use="yes" />
   </resource>
 
   <resource name="dreameye">
diff --git a/scripts/tests/e2/e2features.lua b/scripts/tests/e2/e2features.lua
index c88dfdf22..5af956980 100644
--- a/scripts/tests/e2/e2features.lua
+++ b/scripts/tests/e2/e2features.lua
@@ -230,12 +230,24 @@ function test_no_uruk()
   assert_equal(f1.race, "orc")
 end
 
+function test_meow()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u:add_item("aoc", 1)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Katzenamulett")
+    process_orders()
+    assert_equal(1, u:get_item("aoc"))
+    assert_equal(1, r:count_msg_type('meow'))
+end
+
 function test_ointment()
     local r = region.create(0, 0, "plain")
     local f = faction.create("noreply@eressea.de", "human", "de")
     local u = unit.create(f, r, 1)
     local hp = u.hp
-    u.hp = 0
+    u.hp = 1
     u:add_item("ointment", 1)
     u:clear_orders()
     u:add_order("BENUTZEN 1 Wundsalbe")
diff --git a/src/bind_region.c b/src/bind_region.c
index 9e4e2f778..05c0e3842 100644
--- a/src/bind_region.c
+++ b/src/bind_region.c
@@ -29,6 +29,7 @@ without prior permission by the authors of Eressea.
 #include <kernel/ship.h>
 #include <kernel/plane.h>
 #include <kernel/terrain.h>
+#include <kernel/messages.h>
 #include <modules/autoseed.h>
 #include <attributes/key.h>
 #include <attributes/racename.h>
@@ -46,6 +47,23 @@ without prior permission by the authors of Eressea.
 #include <string.h>
 #include <stdlib.h>
 
+static int tolua_region_count_msg_type(lua_State *L) {
+    region *self = (region *)tolua_tousertype(L, 1, 0);
+    const char *str = tolua_tostring(L, 2, 0);
+    int n = 0;
+    if (self->msgs) {
+        mlist * ml = self->msgs->begin;
+        while (ml) {
+            if (strcmp(str, ml->msg->type->name) == 0) {
+                ++n;
+            }
+            ml = ml->next;
+        }
+    }
+    lua_pushinteger(L, n);
+    return 1;
+}
+
 int tolua_regionlist_next(lua_State * L)
 {
     region **region_ptr = (region **)lua_touserdata(L, lua_upvalueindex(1));
@@ -671,6 +689,8 @@ void tolua_region_open(lua_State * L)
             tolua_function(L, TOLUA_CAST "destroy", tolua_region_destroy);
             tolua_function(L, TOLUA_CAST "__tostring", tolua_region_tostring);
 
+            tolua_function(L, TOLUA_CAST "count_msg_type", tolua_region_count_msg_type);
+
             /* flags */
             tolua_variable(L, TOLUA_CAST "blocked", tolua_region_get_blocked, tolua_region_set_blocked);
 
diff --git a/src/kernel/item.c b/src/kernel/item.c
index 1fa006c73..e0f8f64cc 100644
--- a/src/kernel/item.c
+++ b/src/kernel/item.c
@@ -1289,7 +1289,7 @@ void register_resources(void)
 
     register_item_use(use_potion_delayed, "use_p2");
     register_item_use(use_tacticcrystal, "use_tacticcrystal");
-    register_item_use(use_birthdayamulet, "use_birthdayamulet");
+    register_item_use(use_birthdayamulet, "use_aoc");
     register_item_use(use_warmthpotion, "use_nestwarmth");
     register_item_use(use_bloodpotion, "use_peasantblood");
     register_item_use(use_healingpotion, "use_ointment");
diff --git a/src/kernel/xmlreader.c b/src/kernel/xmlreader.c
index c4ce845a5..69e9c22d0 100644
--- a/src/kernel/xmlreader.c
+++ b/src/kernel/xmlreader.c
@@ -855,6 +855,7 @@ static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype)
             struct order *))fun;
         }
         else if (strcmp((const char *)propValue, "use") == 0) {
+            log_error("%s has a use function", rtype->_name);
             itype->flags |= ITF_CANUSE;
             itype->use =
                 (int(*)(struct unit *, const struct item_type *, int,

From 5ffe60193bdbdaad2b72f019b020bc2dd1743f76 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno@eressea.de>
Date: Mon, 27 Feb 2017 14:10:12 +0100
Subject: [PATCH 16/42] move items with tests to items.c

---
 src/items.c       | 134 ++++++++++++++++++++++++++++++++++++++++++++--
 src/kernel/item.c | 134 ++--------------------------------------------
 2 files changed, 133 insertions(+), 135 deletions(-)

diff --git a/src/items.c b/src/items.c
index bb5009038..cae5ea642 100644
--- a/src/items.c
+++ b/src/items.c
@@ -1,6 +1,7 @@
 #include <platform.h>
 #include "items.h"
 
+#include "alchemy.h"
 #include "study.h"
 #include "economy.h"
 #include "move.h"
@@ -14,6 +15,7 @@
 #include <kernel/order.h>
 #include <kernel/plane.h>
 #include <kernel/pool.h>
+#include <kernel/race.h>
 #include <kernel/region.h>
 #include <kernel/ship.h>
 #include <kernel/spell.h>
@@ -21,9 +23,15 @@
 
 #include <items/demonseye.h>
 
+/* triggers includes */
+#include <triggers/changerace.h>
+#include <triggers/timeout.h>
+
 #include <util/attrib.h>
+#include <util/event.h>
 #include <util/parser.h>
 #include <util/rand.h>
+#include <util/rng.h>
 
 #include <assert.h>
 #include <limits.h>
@@ -125,15 +133,15 @@ struct order *ord)
         UNUSED_ARG(ord);
         assert(sp);
 
-        /* Reduziert die St�rke jedes Spruchs um effect */
+        /* Reduziert die St�rke jedes Spruchs um effect */
         effect = 5;
 
-        /* H�lt Spr�che bis zu einem summierten Gesamtlevel von power aus.
+        /* H�lt Spr�che bis zu einem summierten Gesamtlevel von power aus.
          * Jeder Zauber reduziert die 'Lebenskraft' (vigour) der Antimagiezone
          * um seine Stufe */
         force = effect * 20;     /* Stufe 5 =~ 100 */
 
-        /* Regionszauber aufl�sen */
+        /* Regionszauber aufl�sen */
         while (*ap && force > 0) {
             curse *c;
             attrib *a = *ap;
@@ -145,7 +153,7 @@ struct order *ord)
             }
             c = (curse *)a->data.v;
 
-            /* Immunit�t pr�fen */
+            /* Immunit�t pr�fen */
             if (c_flags(c) & CURSE_IMMUNE) {
                 do {
                     ap = &(*ap)->next;
@@ -264,6 +272,118 @@ int amount, struct order *ord)
     return 0;
 }
 
+static int
+use_birthdayamulet(unit * u, const struct item_type *itype, int amount,
+struct order *ord)
+{
+    direction_t d;
+    message *msg = msg_message("meow", "");
+
+    UNUSED_ARG(ord);
+    UNUSED_ARG(amount);
+    UNUSED_ARG(itype);
+
+    add_message(&u->region->msgs, msg);
+    for (d = 0; d < MAXDIRECTIONS; d++) {
+        region *tr = rconnect(u->region, d);
+        if (tr)
+            add_message(&tr->msgs, msg);
+    }
+    msg_release(msg);
+    return 0;
+}
+
+static int use_foolpotion(unit *u, const item_type *itype, int amount,
+    struct order *ord)
+{
+    int targetno = read_unitid(u->faction, u->region);
+    unit *target = findunit(targetno);
+    if (target == NULL || u->region != target->region) {
+        ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found",
+            ""));
+        return ECUSTOM;
+    }
+    if (effskill(u, SK_STEALTH, 0) <= effskill(target, SK_PERCEPTION, 0)) {
+        cmistake(u, ord, 64, MSG_EVENT);
+        return ECUSTOM;
+    }
+    ADDMSG(&u->faction->msgs, msg_message("givedumb",
+        "unit recipient amount", u, target, amount));
+
+    change_effect(target, itype->rtype->ptype, amount);
+    use_pooled(u, itype->rtype, GET_DEFAULT, amount);
+    return 0;
+}
+
+static int
+use_bloodpotion(struct unit *u, const struct item_type *itype, int amount,
+struct order *ord)
+{
+    if (u->number == 0 || u_race(u) == get_race(RC_DAEMON)) {
+        change_effect(u, itype->rtype->ptype, 100 * amount);
+    }
+    else {
+        const race *irace = u_irace(u);
+        if (irace == u_race(u)) {
+            const race *rcfailure = rc_find("smurf");
+            if (!rcfailure) {
+                rcfailure = rc_find("toad");
+            }
+            if (rcfailure) {
+                trigger *trestore = trigger_changerace(u, u_race(u), irace);
+                if (trestore) {
+                    int duration = 2 + rng_int() % 8;
+
+                    add_trigger(&u->attribs, "timer", trigger_timeout(duration,
+                        trestore));
+                    u->irace = NULL;
+                    u_setrace(u, rcfailure);
+                }
+            }
+        }
+    }
+    use_pooled(u, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK,
+        amount);
+    usetpotionuse(u, itype->rtype->ptype);
+
+    ADDMSG(&u->faction->msgs, msg_message("usepotion",
+        "unit potion", u, itype->rtype));
+    return 0;
+}
+
+static int heal(unit * user, int effect)
+{
+    int req = unit_max_hp(user) * user->number - user->hp;
+    if (req > 0) {
+        req = MIN(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->rtype->ptype);
+
+    ADDMSG(&user->faction->msgs, msg_message("usepotion",
+        "unit potion", user, itype->rtype));
+    return 0;
+}
+
 void register_itemfunctions(void)
 {
     register_demonseye();
@@ -274,4 +394,10 @@ void register_itemfunctions(void)
     register_item_use(use_instantartacademy, "use_instantartacademy");
     register_item_use(use_bagpipeoffear, "use_bagpipeoffear");
     register_item_use(use_aurapotion50, "use_aurapotion50");
+
+    /* have tests: */
+    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");
 }
diff --git a/src/kernel/item.c b/src/kernel/item.c
index e0f8f64cc..ecad829ca 100644
--- a/src/kernel/item.c
+++ b/src/kernel/item.c
@@ -34,23 +34,18 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include "terrain.h"
 #include "unit.h"
 
-/* triggers includes */
-#include <triggers/changerace.h>
-#include <triggers/timeout.h>
-
 /* util includes */
 #include <util/attrib.h>
 #include <util/base36.h>
-#include <critbit.h>
-#include <util/event.h>
 #include <util/functions.h>
 #include <util/goodies.h>
 #include <util/log.h>
 #include <util/language.h>
 #include <util/message.h>
-#include <util/umlaut.h>
 #include <util/rng.h>
+#include <util/umlaut.h>
 
+#include <critbit.h>
 #include <storage.h>
 
 /* libc includes */
@@ -675,27 +670,6 @@ int set_item(unit * u, const item_type *itype, int value)
     return value;
 }
 
-static int
-use_birthdayamulet(unit * u, const struct item_type *itype, int amount,
-struct order *ord)
-{
-    direction_t d;
-    message *msg = msg_message("meow", "");
-
-    UNUSED_ARG(ord);
-    UNUSED_ARG(amount);
-    UNUSED_ARG(itype);
-
-    add_message(&u->region->msgs, msg);
-    for (d = 0; d < MAXDIRECTIONS; d++) {
-        region *tr = rconnect(u->region, d);
-        if (tr)
-            add_message(&tr->msgs, msg);
-    }
-    msg_release(msg);
-    return 0;
-}
-
 /* t_item::flags */
 #define FL_ITEM_CURSED  (1<<0)
 #define FL_ITEM_NOTLOST (1<<1)
@@ -766,17 +740,6 @@ mod_dwarves_only(const unit * u, const region * r, skill_t sk, int value)
     return -118;
 }
 
-static int heal(unit * user, int effect)
-{
-    int req = unit_max_hp(user) * user->number - user->hp;
-    if (req > 0) {
-        req = MIN(req, effect);
-        effect -= req;
-        user->hp += req;
-    }
-    return effect;
-}
-
 void
 register_item_give(int(*foo) (struct unit *, struct unit *,
 const struct item_type *, int, struct order *), const char *name)
@@ -791,35 +754,6 @@ struct order *), const char *name)
     register_function((pf_generic)foo, name);
 }
 
-void
-register_item_useonother(int(*foo) (struct unit *, int,
-const struct item_type *, int, struct order *), const char *name)
-{
-    register_function((pf_generic)foo, name);
-}
-
-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->rtype->ptype);
-
-    ADDMSG(&user->faction->msgs, msg_message("usepotion",
-        "unit potion", user, itype->rtype));
-    return 0;
-}
-
 static int use_warmthpotion(unit *u, const item_type *itype,
     int amount, struct order *ord)
 {
@@ -840,64 +774,6 @@ static int use_warmthpotion(unit *u, const item_type *itype,
     return 0;
 }
 
-static int use_foolpotion(unit *u, const item_type *itype, int amount,
-    struct order *ord)
-{
-    int targetno = read_unitid(u->faction, u->region);
-    unit *target = findunit(targetno);
-    if (target == NULL || u->region != target->region) {
-        ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found",
-            ""));
-        return ECUSTOM;
-    }
-    if (effskill(u, SK_STEALTH, 0) <= effskill(target, SK_PERCEPTION, 0)) {
-        cmistake(u, ord, 64, MSG_EVENT);
-        return ECUSTOM;
-    }
-    ADDMSG(&u->faction->msgs, msg_message("givedumb",
-        "unit recipient amount", u, target, amount));
-
-    change_effect(target, itype->rtype->ptype, amount);
-    use_pooled(u, itype->rtype, GET_DEFAULT, amount);
-    return 0;
-}
-
-static int
-use_bloodpotion(struct unit *u, const struct item_type *itype, int amount,
-struct order *ord)
-{
-    if (u->number == 0 || u_race(u) == get_race(RC_DAEMON)) {
-        change_effect(u, itype->rtype->ptype, 100 * amount);
-    }
-    else {
-        const race *irace = u_irace(u);
-        if (irace == u_race(u)) {
-            const race *rcfailure = rc_find("smurf");
-            if (!rcfailure) {
-                rcfailure = rc_find("toad");
-            }
-            if (rcfailure) {
-                trigger *trestore = trigger_changerace(u, u_race(u), irace);
-                if (trestore) {
-                    int duration = 2 + rng_int() % 8;
-
-                    add_trigger(&u->attribs, "timer", trigger_timeout(duration,
-                        trestore));
-                    u->irace = NULL;
-                    u_setrace(u, rcfailure);
-                }
-            }
-        }
-    }
-    use_pooled(u, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK,
-        amount);
-    usetpotionuse(u, itype->rtype->ptype);
-
-    ADDMSG(&u->faction->msgs, msg_message("usepotion",
-        "unit potion", u, itype->rtype));
-    return 0;
-}
-
 #include <attributes/fleechance.h>
 static int
 use_mistletoe(struct unit *user, const struct item_type *itype, int amount,
@@ -1288,12 +1164,8 @@ void register_resources(void)
     register_function((pf_generic)res_changeaura, "changeaura");
 
     register_item_use(use_potion_delayed, "use_p2");
-    register_item_use(use_tacticcrystal, "use_tacticcrystal");
-    register_item_use(use_birthdayamulet, "use_aoc");
     register_item_use(use_warmthpotion, "use_nestwarmth");
-    register_item_use(use_bloodpotion, "use_peasantblood");
-    register_item_use(use_healingpotion, "use_ointment");
-    register_item_use(use_foolpotion, "use_p7");
+    register_item_use(use_tacticcrystal, "use_tacticcrystal");
     register_item_use(use_mistletoe, "usemistletoe");
     register_item_use(use_magicboost, "usemagicboost");
     register_item_use(use_snowball, "usesnowball");

From db0cbf34c713a25ff2c8743ad808a5a94577c2a6 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno@eressea.de>
Date: Mon, 27 Feb 2017 14:17:38 +0100
Subject: [PATCH 17/42] test for aurapotion

---
 scripts/tests/e2/e2features.lua | 17 +++++++++++++++++
 src/items.c                     |  2 +-
 2 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/scripts/tests/e2/e2features.lua b/scripts/tests/e2/e2features.lua
index 5af956980..59e93f86b 100644
--- a/scripts/tests/e2/e2features.lua
+++ b/scripts/tests/e2/e2features.lua
@@ -242,6 +242,23 @@ function test_meow()
     assert_equal(1, r:count_msg_type('meow'))
 end
 
+function test_aurapotion50()
+    eressea.settings.set("magic.regeneration.enable", "0")
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u:add_item("aurapotion50", 1)
+    u:set_skill('magic', 10);
+    u.magic = 'gwyrrd'
+    u.aura = 0
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Auratrank")
+    process_orders()
+    assert_equal(0, u:get_item("aurapotion50"))
+    assert_equal(1, f:count_msg_type('aurapotion50'))
+    assert_equal(50, u.aura)
+end
+
 function test_ointment()
     local r = region.create(0, 0, "plain")
     local f = faction.create("noreply@eressea.de", "human", "de")
diff --git a/src/items.c b/src/items.c
index cae5ea642..75cb79b68 100644
--- a/src/items.c
+++ b/src/items.c
@@ -393,9 +393,9 @@ void register_itemfunctions(void)
     register_item_use(use_speedsail, "use_speedsail");
     register_item_use(use_instantartacademy, "use_instantartacademy");
     register_item_use(use_bagpipeoffear, "use_bagpipeoffear");
-    register_item_use(use_aurapotion50, "use_aurapotion50");
 
     /* have tests: */
+    register_item_use(use_aurapotion50, "use_aurapotion50");
     register_item_use(use_birthdayamulet, "use_aoc");
     register_item_use(use_foolpotion, "use_p7");
     register_item_use(use_bloodpotion, "use_peasantblood");

From 3d4860c0337933841b39820edeba3b3d1657bbc7 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno@eressea.de>
Date: Mon, 27 Feb 2017 14:19:25 +0100
Subject: [PATCH 18/42] remove more of the artrewards

---
 src/items.c | 46 ----------------------------------------------
 1 file changed, 46 deletions(-)

diff --git a/src/items.c b/src/items.c
index 75cb79b68..b01fe21db 100644
--- a/src/items.c
+++ b/src/items.c
@@ -179,50 +179,6 @@ struct order *ord)
     return 0;
 }
 
-static int
-use_instantartsculpture(struct unit *u, const struct item_type *itype,
-int amount, struct order *ord)
-{
-    building *b;
-
-    if (u->region->land == NULL) {
-        ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_onlandonly", ""));
-        return -1;
-    }
-
-    b = new_building(bt_find("artsculpture"), u->region, u->faction->locale);
-    b->size = 100;
-
-    ADDMSG(&u->region->msgs, msg_message("artsculpture_create", "unit region",
-        u, u->region));
-
-    use_pooled(u, itype->rtype, GET_DEFAULT, 1);
-
-    return 0;
-}
-
-static int
-use_instantartacademy(struct unit *u, const struct item_type *itype,
-int amount, struct order *ord)
-{
-    building *b;
-
-    if (u->region->land == NULL) {
-        ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_onlandonly", ""));
-        return -1;
-    }
-
-    b = new_building(bt_find("artacademy"), u->region, u->faction->locale);
-    b->size = 100;
-
-    ADDMSG(&u->region->msgs, msg_message("artacademy_create", "unit region", u,
-        u->region));
-
-    use_pooled(u, itype->rtype, GET_DEFAULT, 1);
-
-    return 0;
-}
-
 #define BAGPIPEFRACTION dice_rand("2d4+2")
 #define BAGPIPEDURATION dice_rand("2d10+4")
 
@@ -388,10 +344,8 @@ void register_itemfunctions(void)
 {
     register_demonseye();
     register_item_use(use_antimagiccrystal, "use_antimagiccrystal");
-    register_item_use(use_instantartsculpture, "use_instantartsculpture");
     register_item_use(use_studypotion, "use_studypotion");
     register_item_use(use_speedsail, "use_speedsail");
-    register_item_use(use_instantartacademy, "use_instantartacademy");
     register_item_use(use_bagpipeoffear, "use_bagpipeoffear");
 
     /* have tests: */

From 108501e398a29106d1ba6a86e2730210d0ee5cdc Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno@eressea.de>
Date: Mon, 27 Feb 2017 15:14:52 +0100
Subject: [PATCH 19/42] tests for more items

---
 res/core/common/items.xml       |  4 +--
 scripts/tests/e2/e2features.lua | 52 +++++++++++++++++++++++++++++++++
 src/items.c                     |  7 +++--
 3 files changed, 57 insertions(+), 6 deletions(-)

diff --git a/res/core/common/items.xml b/res/core/common/items.xml
index 8745b6e59..30926a844 100644
--- a/res/core/common/items.xml
+++ b/res/core/common/items.xml
@@ -128,9 +128,7 @@
 
 <!-- items -->
   <resource name="antimagic" appearance="amulet">
-    <item weight="0" score="2000">
-      <function name="use" value="use_antimagiccrystal"/>
-    </item>
+	  <item weight="0" score="2000" use="yes" />
   </resource>
 
   <resource name="catapultammo">
diff --git a/scripts/tests/e2/e2features.lua b/scripts/tests/e2/e2features.lua
index 59e93f86b..a942480a9 100644
--- a/scripts/tests/e2/e2features.lua
+++ b/scripts/tests/e2/e2features.lua
@@ -259,6 +259,58 @@ function test_aurapotion50()
     assert_equal(50, u.aura)
 end
 
+function test_bagpipe()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u:add_item("bagpipeoffear", 1)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Dudelsack")
+    process_orders()
+    assert_equal(1, u:get_item("bagpipeoffear"))
+    assert_equal(1, f:count_msg_type('bagpipeoffear_faction'))
+    assert_equal(1, r:count_msg_type('bagpipeoffear_region'))
+end
+
+function test_speedsail()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u.ship = ship.create(r, "boat")
+    u:add_item("speedsail", 2)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Sonnensegel")
+    process_orders()
+    assert_equal(1, u:get_item("speedsail"))
+    assert_equal(1, f:count_msg_type('use_speedsail'))
+end
+
+--[[
+function test_studypotion()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u:add_item("studypotion", 2)
+    u:clear_orders()
+    u:add_order("LERNE Unterhaltung")
+    u:add_order("BENUTZEN 1 Lerntrank")
+    process_orders()
+    assert_equal(1, u:get_item("studypotion"))
+end
+]]--
+
+function test_antimagic()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u:add_item("antimagic", 2)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Antimagiekristall")
+    process_orders()
+    assert_equal(1, r:count_msg_type('use_antimagiccrystal'))
+    assert_equal(1, u:get_item("antimagic"))
+end
+
 function test_ointment()
     local r = region.create(0, 0, "plain")
     local f = faction.create("noreply@eressea.de", "human", "de")
diff --git a/src/items.c b/src/items.c
index b01fe21db..9833ac6f7 100644
--- a/src/items.c
+++ b/src/items.c
@@ -29,6 +29,7 @@
 
 #include <util/attrib.h>
 #include <util/event.h>
+#include <util/log.h>
 #include <util/parser.h>
 #include <util/rand.h>
 #include <util/rng.h>
@@ -343,12 +344,12 @@ struct order *ord)
 void register_itemfunctions(void)
 {
     register_demonseye();
-    register_item_use(use_antimagiccrystal, "use_antimagiccrystal");
     register_item_use(use_studypotion, "use_studypotion");
-    register_item_use(use_speedsail, "use_speedsail");
-    register_item_use(use_bagpipeoffear, "use_bagpipeoffear");
 
     /* have tests: */
+    register_item_use(use_antimagiccrystal, "use_antimagic");
+    register_item_use(use_speedsail, "use_speedsail");
+    register_item_use(use_bagpipeoffear, "use_bagpipeoffear");
     register_item_use(use_aurapotion50, "use_aurapotion50");
     register_item_use(use_birthdayamulet, "use_aoc");
     register_item_use(use_foolpotion, "use_p7");

From 1a0992e37b1385ba91b633a58d7d7e982a3ebc63 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Mon, 27 Feb 2017 18:21:41 +0100
Subject: [PATCH 20/42] =?UTF-8?q?remove=20remainders=20of=20the=20arena.?=
 =?UTF-8?q?=20Auge=20des=20Drachens=20haben=20noch=20einige=20Spieler,=20d?=
 =?UTF-8?q?arf=20nicht=20gel=C3=B6scht=20werden.=20move=20item=20tests=20t?=
 =?UTF-8?q?o=20tests/items.lua?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 ...txt => gprof-v3.10.0-163-gdfab45d.profile} |   0
 res/core/common/items.xml                     |   5 +
 res/eressea/items.xml                         |  20 +-
 res/eressea/strings.xml                       |  35 ---
 scripts/tests/e2/e2features.lua               | 165 ------------
 scripts/tests/e2/init.lua                     |   1 +
 scripts/tests/items.lua                       | 186 +++++++++++++
 src/attributes/attributes.c                   |   1 +
 src/eressea.c                                 |   6 -
 src/gmtool.c                                  |   3 -
 src/items.c                                   |   9 +-
 src/modules/CMakeLists.txt                    |   1 -
 src/modules/arena.c                           | 255 ------------------
 src/modules/arena.h                           |  39 ---
 src/settings.h                                |   1 -
 15 files changed, 198 insertions(+), 529 deletions(-)
 rename doc/{gprof-v3.10.0-163-gdfab45d.txt => gprof-v3.10.0-163-gdfab45d.profile} (100%)
 create mode 100644 scripts/tests/items.lua
 delete mode 100644 src/modules/arena.c
 delete mode 100644 src/modules/arena.h

diff --git a/doc/gprof-v3.10.0-163-gdfab45d.txt b/doc/gprof-v3.10.0-163-gdfab45d.profile
similarity index 100%
rename from doc/gprof-v3.10.0-163-gdfab45d.txt
rename to doc/gprof-v3.10.0-163-gdfab45d.profile
diff --git a/res/core/common/items.xml b/res/core/common/items.xml
index 30926a844..63a07d88f 100644
--- a/res/core/common/items.xml
+++ b/res/core/common/items.xml
@@ -98,6 +98,11 @@
   </resource>
 
 <!-- XE items -->
+  <resource name="studypotion" appearance="vial">
+    <!-- gives user one free learning attempt -->
+    <item weight="0" use="yes" />
+  </resource>
+
   <resource name="skillpotion" appearance="vial">
     <!-- gives user one free learning attempt -->
     <item weight="0">
diff --git a/res/eressea/items.xml b/res/eressea/items.xml
index c0b8b5126..007f0277c 100644
--- a/res/eressea/items.xml
+++ b/res/eressea/items.xml
@@ -86,27 +86,9 @@
     <item notlost="yes" cursed="true" weight="0"/>
   </resource>
 
-  <resource name="ao_daemon">
-    <!-- summons igjarjuk -->
-    <item weight="0" score="6000" notlost="true" cursed="true">
-      <function name="use" value="useigjarjuk"/>
-      <function name="give" value="giveigjarjuk"/>
-    </item>
-  </resource>
-
-  <resource name="griphonwing">
-    <!-- this lets you leave the arena -->
-    <item weight="0" score="6000" notlost="true" cursed="true">
-      <function name="use" value="leave_arena"/>
-      <function name="give" value="giveigjarjuk"/>
-    </item>
-  </resource>
-
   <resource name="eyeofdragon">
     <!-- the arena gate, for one-time entry -->
-    <item weight="0" score="0">
-      <function name="use" value="enter_arena"/>
-    </item>
+    <item weight="0" score="0"/>
   </resource>
 
   <resource name="jadee_ring" appearance="ring">
diff --git a/res/eressea/strings.xml b/res/eressea/strings.xml
index f7497b810..4869014c5 100644
--- a/res/eressea/strings.xml
+++ b/res/eressea/strings.xml
@@ -116,18 +116,6 @@
       <text locale="de">Eine Geburtstagstorte mit 10 Kerzen. Herzlichen Glückwunsch, Eressea!</text>
       <text locale="en">A birthday cake with 10 candles. Happy Birthday, Eressea!</text>
     </string>
-    <string name="griphonwing">
-      <text locale="de">Dieses Fluggerät aus der Schmiede der Zwerge von Celeband galt wie die
-        'Ebene der Herausforderung' seit Urzeiten als verschollen, ja man
-        zweifelte seine Existenz an. Die Sage überliefert, das derjenige, der
-        sie auf der Spitze des Turmes seiner Gesinnung benutzt, als einziger
-        die 'Ebene der Herausforderungen' verlassen kann.</text>
-    </string>
-    <string name="ao_daemon">
-      <text locale="de">Glückwunsch, mein Kind. Du bist im Besitz des mächtigsten
-        Artefaktes Eresseas. Ein Fluch, sagt man, liege auf ihm, denn
-        niemand hat es bisher lange sein Eigen genannt...</text>
-    </string>
     <string name="cookie">
       <text locale="de">Kleines trockenes Dauergebäck, m od. s; - u. -es, - u. -e</text>
     </string>
@@ -374,29 +362,6 @@
   </string>
   <!-- art rewards end -->
 
-  <!-- igjarjuk queste begin -->
-  <string name="ao_daemon">
-    <text locale="de">Auge des Dämon</text>
-    <text locale="en">eye of the demon</text>
-    <text locale="fr">oeil du démon</text>
-  </string>
-  <string name="ao_daemon_p">
-    <text locale="de">Augen des Dämon</text>
-    <text locale="en">eyes of the demon</text>
-    <text locale="fr">oeil du démon</text>
-  </string>
-  <string name="griphonwing">
-    <text locale="de">Schwinge des Greifen</text>
-    <text locale="en">wing of the gryphon</text>
-    <text locale="fr">aile du griffon</text>
-  </string>
-  <string name="griphonwing_p">
-    <text locale="de">Schwingen des Greifen</text>
-    <text locale="en">wings of the gryphon</text>
-    <text locale="fr">ailes du griffon</text>
-  </string>
-  <!-- igjarjuk queste end -->
-
   <string name="rm_adamantium">
     <text locale="de">Adamantium</text>
     <text locale="en">adamantium</text>
diff --git a/scripts/tests/e2/e2features.lua b/scripts/tests/e2/e2features.lua
index a942480a9..da2a98d55 100644
--- a/scripts/tests/e2/e2features.lua
+++ b/scripts/tests/e2/e2features.lua
@@ -230,171 +230,6 @@ function test_no_uruk()
   assert_equal(f1.race, "orc")
 end
 
-function test_meow()
-    local r = region.create(0, 0, "plain")
-    local f = faction.create("noreply@eressea.de", "human", "de")
-    local u = unit.create(f, r, 1)
-    u:add_item("aoc", 1)
-    u:clear_orders()
-    u:add_order("BENUTZEN 1 Katzenamulett")
-    process_orders()
-    assert_equal(1, u:get_item("aoc"))
-    assert_equal(1, r:count_msg_type('meow'))
-end
-
-function test_aurapotion50()
-    eressea.settings.set("magic.regeneration.enable", "0")
-    local r = region.create(0, 0, "plain")
-    local f = faction.create("noreply@eressea.de", "human", "de")
-    local u = unit.create(f, r, 1)
-    u:add_item("aurapotion50", 1)
-    u:set_skill('magic', 10);
-    u.magic = 'gwyrrd'
-    u.aura = 0
-    u:clear_orders()
-    u:add_order("BENUTZEN 1 Auratrank")
-    process_orders()
-    assert_equal(0, u:get_item("aurapotion50"))
-    assert_equal(1, f:count_msg_type('aurapotion50'))
-    assert_equal(50, u.aura)
-end
-
-function test_bagpipe()
-    local r = region.create(0, 0, "plain")
-    local f = faction.create("noreply@eressea.de", "human", "de")
-    local u = unit.create(f, r, 1)
-    u:add_item("bagpipeoffear", 1)
-    u:clear_orders()
-    u:add_order("BENUTZEN 1 Dudelsack")
-    process_orders()
-    assert_equal(1, u:get_item("bagpipeoffear"))
-    assert_equal(1, f:count_msg_type('bagpipeoffear_faction'))
-    assert_equal(1, r:count_msg_type('bagpipeoffear_region'))
-end
-
-function test_speedsail()
-    local r = region.create(0, 0, "plain")
-    local f = faction.create("noreply@eressea.de", "human", "de")
-    local u = unit.create(f, r, 1)
-    u.ship = ship.create(r, "boat")
-    u:add_item("speedsail", 2)
-    u:clear_orders()
-    u:add_order("BENUTZEN 1 Sonnensegel")
-    process_orders()
-    assert_equal(1, u:get_item("speedsail"))
-    assert_equal(1, f:count_msg_type('use_speedsail'))
-end
-
---[[
-function test_studypotion()
-    local r = region.create(0, 0, "plain")
-    local f = faction.create("noreply@eressea.de", "human", "de")
-    local u = unit.create(f, r, 1)
-    u:add_item("studypotion", 2)
-    u:clear_orders()
-    u:add_order("LERNE Unterhaltung")
-    u:add_order("BENUTZEN 1 Lerntrank")
-    process_orders()
-    assert_equal(1, u:get_item("studypotion"))
-end
-]]--
-
-function test_antimagic()
-    local r = region.create(0, 0, "plain")
-    local f = faction.create("noreply@eressea.de", "human", "de")
-    local u = unit.create(f, r, 1)
-    u:add_item("antimagic", 2)
-    u:clear_orders()
-    u:add_order("BENUTZEN 1 Antimagiekristall")
-    process_orders()
-    assert_equal(1, r:count_msg_type('use_antimagiccrystal'))
-    assert_equal(1, u:get_item("antimagic"))
-end
-
-function test_ointment()
-    local r = region.create(0, 0, "plain")
-    local f = faction.create("noreply@eressea.de", "human", "de")
-    local u = unit.create(f, r, 1)
-    local hp = u.hp
-    u.hp = 1
-    u:add_item("ointment", 1)
-    u:clear_orders()
-    u:add_order("BENUTZEN 1 Wundsalbe")
-    process_orders()
-    assert_equal(0, u:get_item("ointment"))
-    assert_equal(1, f:count_msg_type('usepotion'))
-    assert_equal(hp, u.hp)
-end
-
-function test_bloodpotion_demon()
-    local r = region.create(0, 0, "plain")
-    local f = faction.create("noreply@eressea.de", "demon", "de")
-    local u = unit.create(f, r, 1)
-    u:add_item("peasantblood", 1)
-    u:clear_orders()
-    u:add_order("BENUTZEN 1 Bauernblut")
-    process_orders()
-    assert_equal(0, u:get_item("peasantblood"))
-    assert_equal(1, f:count_msg_type('usepotion'))
-    assert_equal("demon", u.race)
-end
-
-function test_bloodpotion_other()
-    local r = region.create(0, 0, "plain")
-    local f = faction.create("noreply@eressea.de", "human", "de")
-    local u = unit.create(f, r, 1)
-    u:add_item("peasantblood", 1)
-    u:clear_orders()
-    u:add_order("BENUTZEN 1 Bauernblut")
-    process_orders()
-    assert_equal(0, u:get_item("peasantblood"))
-    assert_equal(1, f:count_msg_type('usepotion'))
-    assert_equal("smurf", u.race)
-end
-
-function test_foolpotion()
-    local r = region.create(0, 0, "plain")
-    local f = faction.create("noreply@eressea.de", "human", "de")
-    local u = unit.create(f, r, 1)
-    u:add_item("p7", 1)
-    u:clear_orders()
-    u:add_order("BENUTZEN 1 Dumpfbackenbrot 4242")
-    process_orders()
-    assert_equal(1, u:get_item("p7"))
-    assert_equal(1, f:count_msg_type('feedback_unit_not_found'))
-    local u2 = unit.create(f, r, 1)
-    
-    u:clear_orders()
-    u:add_order("BENUTZEN 1 Dumpfbackenbrot " .. itoa36(u2.id))
-    process_orders()
-    assert_equal(1, u:get_item("p7"))
-    assert_equal(1, f:count_msg_type('error64'))
-
-    u:set_skill("stealth", 1);
-    process_orders()
-    assert_equal(0, u:get_item("p7"))
-    assert_equal(1, f:count_msg_type('givedumb'))
-end
-
-function test_snowman()
-    local r = region.create(0, 0, "glacier")
-    local f = faction.create("noreply@eressea.de", "human", "de")
-    local u = unit.create(f, r, 1)
-    u:add_item("snowman", 1)
-    u:clear_orders()
-    u:add_order("BENUTZEN 1 Schneemann")
-    process_orders()
-    for u2 in r.units do
-        if u2.id~=u.id then
-            assert_equal("snowman", u2.race)
-            assert_equal(1000, u2.hp)
-            u = nil
-            break
-        end
-    end
-    assert_equal(nil, u)
-end
-
 function test_block_movement()
   eressea.settings.set("rules.guard.base_stop_prob", "0.3")
   eressea.settings.set("rules.guard.amulet_stop_prob", "0.0")
diff --git a/scripts/tests/e2/init.lua b/scripts/tests/e2/init.lua
index afaac7209..4d916d1a9 100644
--- a/scripts/tests/e2/init.lua
+++ b/scripts/tests/e2/init.lua
@@ -8,6 +8,7 @@ require 'tests.e2.destroy'
 require 'tests.e2.guard'
 require 'tests.e2.spells'
 require 'tests.e2.stealth'
+require 'tests.items'
 require 'tests.orders'
 require 'tests.common'
 require 'tests.report'
diff --git a/scripts/tests/items.lua b/scripts/tests/items.lua
new file mode 100644
index 000000000..15d638a63
--- /dev/null
+++ b/scripts/tests/items.lua
@@ -0,0 +1,186 @@
+require "lunit"
+
+module("tests.items", package.seeall, lunit.testcase )
+
+function setup()
+    eressea.free_game()
+    eressea.settings.set("nmr.timeout", "0")
+    eressea.settings.set("rules.food.flags", "4")
+    eressea.settings.set("rules.ship.storms", "0")
+    eressea.settings.set("rules.encounters", "0")
+end
+
+function test_meow()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u:add_item("aoc", 1)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Katzenamulett")
+    process_orders()
+    assert_equal(1, u:get_item("aoc"))
+    assert_equal(1, r:count_msg_type('meow'))
+end
+
+function test_aurapotion50()
+    eressea.settings.set("magic.regeneration.enable", "0")
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u:add_item("aurapotion50", 1)
+    u:set_skill('magic', 10);
+    u.magic = 'gwyrrd'
+    u.aura = 0
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Auratrank")
+    process_orders()
+    assert_equal(0, u:get_item("aurapotion50"))
+    assert_equal(1, f:count_msg_type('aurapotion50'))
+    assert_equal(50, u.aura)
+end
+
+function test_bagpipe()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u:add_item("bagpipeoffear", 1)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Dudelsack")
+    process_orders()
+    assert_equal(1, u:get_item("bagpipeoffear"))
+    assert_equal(1, f:count_msg_type('bagpipeoffear_faction'))
+    assert_equal(1, r:count_msg_type('bagpipeoffear_region'))
+end
+
+function test_speedsail()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u.ship = ship.create(r, "boat")
+    u:add_item("speedsail", 2)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Sonnensegel")
+    process_orders()
+    assert_equal(1, u:get_item("speedsail"))
+    assert_equal(1, f:count_msg_type('use_speedsail'))
+end
+
+function test_skillpotion()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u:add_item("skillpotion", 2)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Talenttrunk")
+    process_orders()
+    assert_equal(1, u:get_item("skillpotion"))
+    assert_equal(1, f:count_msg_type('skillpotion_use'))
+end
+
+function test_studypotion()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u:add_item("studypotion", 2)
+    u:clear_orders()
+    u:add_order("LERNE Unterhaltung")
+    u:add_order("BENUTZEN 1 Lerntrank")
+    process_orders()
+    assert_equal(1, u:get_item("studypotion"))
+end
+
+function test_antimagic()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u:add_item("antimagic", 2)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Antimagiekristall")
+    process_orders()
+    assert_equal(1, r:count_msg_type('use_antimagiccrystal'))
+    assert_equal(1, u:get_item("antimagic"))
+end
+
+function test_ointment()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    local hp = u.hp
+    u.hp = 1
+    u:add_item("ointment", 1)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Wundsalbe")
+    process_orders()
+    assert_equal(0, u:get_item("ointment"))
+    assert_equal(1, f:count_msg_type('usepotion'))
+    assert_equal(hp, u.hp)
+end
+
+function test_bloodpotion_demon()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "demon", "de")
+    local u = unit.create(f, r, 1)
+    u:add_item("peasantblood", 1)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Bauernblut")
+    process_orders()
+    assert_equal(0, u:get_item("peasantblood"))
+    assert_equal(1, f:count_msg_type('usepotion'))
+    assert_equal("demon", u.race)
+end
+
+function test_bloodpotion_other()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u:add_item("peasantblood", 1)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Bauernblut")
+    process_orders()
+    assert_equal(0, u:get_item("peasantblood"))
+    assert_equal(1, f:count_msg_type('usepotion'))
+    assert_equal("smurf", u.race)
+end
+
+function test_foolpotion()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u:add_item("p7", 1)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Dumpfbackenbrot 4242")
+    process_orders()
+    assert_equal(1, u:get_item("p7"))
+    assert_equal(1, f:count_msg_type('feedback_unit_not_found'))
+    local u2 = unit.create(f, r, 1)
+    
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Dumpfbackenbrot " .. itoa36(u2.id))
+    process_orders()
+    assert_equal(1, u:get_item("p7"))
+    assert_equal(1, f:count_msg_type('error64'))
+
+    u:set_skill("stealth", 1);
+    process_orders()
+    assert_equal(0, u:get_item("p7"))
+    assert_equal(1, f:count_msg_type('givedumb'))
+end
+
+function test_snowman()
+    local r = region.create(0, 0, "glacier")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u:add_item("snowman", 1)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Schneemann")
+    process_orders()
+    for u2 in r.units do
+        if u2.id~=u.id then
+            assert_equal("snowman", u2.race)
+            assert_equal(1000, u2.hp)
+            u = nil
+            break
+        end
+    end
+    assert_equal(nil, u)
+end
diff --git a/src/attributes/attributes.c b/src/attributes/attributes.c
index 42f5247fe..ae6869df8 100644
--- a/src/attributes/attributes.c
+++ b/src/attributes/attributes.c
@@ -112,6 +112,7 @@ void register_attributes(void)
 
     at_register(&at_germs);
 
+    at_deprecate("hurting", a_readint); /* an old arena attribute */
     at_deprecate("xontormiaexpress", a_readint);    /* required for old datafiles */
     at_deprecate("orcification", a_readint);    /* required for old datafiles */
     at_deprecate("lua", read_ext);    /* required for old datafiles */
diff --git a/src/eressea.c b/src/eressea.c
index 8001355f1..2d53c2511 100755
--- a/src/eressea.c
+++ b/src/eressea.c
@@ -8,9 +8,6 @@
 #if MUSEUM_MODULE
 #include <modules/museum.h>
 #endif
-#if ARENA_MODULE
-#include <modules/arena.h>
-#endif
 #include <triggers/triggers.h>
 #include <util/language.h>
 #include <util/functions.h>
@@ -77,9 +74,6 @@ void game_init(void)
     register_itemfunctions();
 #if MUSEUM_MODULE
     register_museum();
-#endif
-#if ARENA_MODULE
-    register_arena();
 #endif
     wormholes_register();
 
diff --git a/src/gmtool.c b/src/gmtool.c
index 3551c3436..148aafe6d 100644
--- a/src/gmtool.c
+++ b/src/gmtool.c
@@ -26,9 +26,6 @@
 #if MUSEUM_MODULE
 #include <modules/museum.h>
 #endif
-#if ARENA_MODULE
-#include <modules/arena.h>
-#endif
 #include <modules/autoseed.h>
 
 #include <kernel/building.h>
diff --git a/src/items.c b/src/items.c
index 9833ac6f7..197240d3e 100644
--- a/src/items.c
+++ b/src/items.c
@@ -70,9 +70,9 @@ struct order *ord)
             if (amount > MAXGAIN) {
                 amount = MAXGAIN;
             }
-            teach->value += amount * 30;
-            if (teach->value > MAXGAIN * 30) {
-                teach->value = MAXGAIN * 30;
+            teach->value += amount * STUDYDAYS;
+            if (teach->value > MAXGAIN * STUDYDAYS) {
+                teach->value = MAXGAIN * STUDYDAYS;
             }
             i_change(&u->items, itype, -amount);
             return 0;
@@ -80,7 +80,6 @@ struct order *ord)
     }
     return EUNUSABLE;
 }
-
 /* END studypotion */
 
 /* BEGIN speedsail */
@@ -344,9 +343,9 @@ struct order *ord)
 void register_itemfunctions(void)
 {
     register_demonseye();
-    register_item_use(use_studypotion, "use_studypotion");
 
     /* have tests: */
+    register_item_use(use_studypotion, "use_studypotion");
     register_item_use(use_antimagiccrystal, "use_antimagic");
     register_item_use(use_speedsail, "use_speedsail");
     register_item_use(use_bagpipeoffear, "use_bagpipeoffear");
diff --git a/src/modules/CMakeLists.txt b/src/modules/CMakeLists.txt
index 633c486f5..82dee9ec4 100644
--- a/src/modules/CMakeLists.txt
+++ b/src/modules/CMakeLists.txt
@@ -1,6 +1,5 @@
 PROJECT(modules C)
 SET(_FILES
-arena.c
 autoseed.c
 gmcmd.c
 museum.c
diff --git a/src/modules/arena.c b/src/modules/arena.c
deleted file mode 100644
index fbc3f0673..000000000
--- a/src/modules/arena.c
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
-Copyright (c) 1998-2015, 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.
-**/
-
-#include <platform.h>
-#include <kernel/config.h>
-
-#if ARENA_MODULE
-#include "arena.h"
-
-/* modules include */
-#include "score.h"
-
-/* items include */
-#include <items/demonseye.h>
-
-/* kernel includes */
-#include <kernel/building.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/terrain.h>
-#include <kernel/terrainid.h>
-#include <kernel/unit.h>
-
-#include <move.h>
-
-/* util include */
-#include <util/attrib.h>
-#include <util/base36.h>
-#include <util/event.h>
-#include <util/gamedata.h>
-#include <util/functions.h>
-#include <util/strings.h>
-#include <util/lists.h>
-#include <util/log.h>
-#include <util/resolve.h>
-#include <util/rng.h>
-
-#include <storage.h>
-
-/* libc include */
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-
-/* exports: */
-plane *arena = NULL;
-
-/* local vars */
-#define CENTRAL_VOLCANO 1
-
-static region *tower_region[6];
-static region *start_region[6];
-
-static region *arena_region(int school)
-{
-    return tower_region[school];
-}
-
-static building *arena_tower(int school)
-{
-    return arena_region(school)->buildings;
-}
-
-static int leave_fail(unit * u)
-{
-    ADDMSG(&u->faction->msgs, msg_message("arena_leave_fail", "unit", u));
-    return 1;
-}
-
-static int
-leave_arena(struct unit *u, const struct item_type *itype, int amount,
-order * ord)
-{
-    if (!u->building && leave_fail(u)) {
-        return -1;
-    }
-    if (u->building != arena_tower(u->faction->magiegebiet) && leave_fail(u)) {
-        return -1;
-    }
-    UNUSED_ARG(amount);
-    UNUSED_ARG(ord);
-    UNUSED_ARG(itype);
-    assert(!"not implemented");
-    return 0;
-}
-
-static int enter_fail(unit * u)
-{
-    ADDMSG(&u->faction->msgs, msg_message("arena_enter_fail", "region unit",
-        u->region, u));
-    return 1;
-}
-
-static int
-enter_arena(unit * u, const item_type * itype, int amount, order * ord)
-{
-    skill_t sk;
-    region *r = u->region;
-    unit *u2;
-    int fee = 2000;
-    UNUSED_ARG(ord);
-    UNUSED_ARG(amount);
-    UNUSED_ARG(itype);
-    if (u->faction->score > fee * 5) {
-        score_t score = u->faction->score / 5;
-        if (score < INT_MAX) {
-            fee = (int)score;
-        } 
-        else {
-            fee = INT_MAX;
-        }
-    }
-    if (getplane(r) == arena)
-        return -1;
-    if (u->number != 1 && enter_fail(u))
-        return -1;
-    if (get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT, fee) < fee
-        && enter_fail(u))
-        return -1;
-    for (sk = 0; sk != MAXSKILLS; ++sk) {
-        if (get_level(u, sk) > 1 && enter_fail(u))
-            return -1;
-    }
-    for (u2 = r->units; u2; u2 = u2->next)
-        if (u2->faction == u->faction)
-            break;
-
-    assert(!"not implemented");
-    /*
-            for (res=0;res!=MAXRESOURCES;++res) if (res!=R_SILVER && res!=R_ARENA_GATE && (is_item(res) || is_herb(res) || is_potion(res))) {
-            int x = get_resource(u, res);
-            if (x) {
-            if (u2) {
-            change_resource(u2, res, x);
-            change_resource(u, res, -x);
-            }
-            else if (enter_fail(u)) return -1;
-            }
-            }
-            */
-    if (get_money(u) > fee) {
-        if (u2)
-            change_money(u2, get_money(u) - fee);
-        else if (enter_fail(u))
-            return -1;
-    }
-    ADDMSG(&u->faction->msgs, msg_message("arena_enter_fail", "region unit",
-        u->region, u));
-    use_pooled(u, itype->rtype, GET_SLACK | GET_RESERVE, 1);
-    use_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT, fee);
-    set_money(u, 109);
-    fset(u, UFL_ANON_FACTION);
-    move_unit(u, start_region[rng_int() % 6], NULL);
-    return 0;
-}
-#ifdef CENTRAL_VOLCANO
-
-static int caldera_handle(trigger * t, void *data)
-{
-    /* call an event handler on caldera.
-     * data.v -> ( variant event, int timer )
-     */
-    building *b = (building *)t->data.v;
-    if (b != NULL) {
-        unit **up = &b->region->units;
-        while (*up) {
-            unit *u = *up;
-            if (u->building == b) {
-                message *msg;
-                if (u->items) {
-                    item **ip = &u->items;
-                    msg = msg_message("caldera_handle_1", "unit items", u, u->items);
-                    while (*ip) {
-                        item *i = *ip;
-                        i_remove(ip, i);
-                        if (*ip == i)
-                            ip = &i->next;
-                    }
-                }
-                else {
-                    msg = msg_message("caldera_handle_0", "unit", u);
-                }
-                add_message(&u->region->msgs, msg);
-                set_number(u, 0);
-            }
-            if (*up == u)
-                up = &u->next;
-        }
-    }
-    else {
-        log_error("could not perform caldera::handle()\n");
-    }
-    UNUSED_ARG(data);
-    return 0;
-}
-
-static void caldera_write(const trigger * t, struct storage *store)
-{
-    building *b = (building *)t->data.v;
-    write_building_reference(b, store);
-}
-
-static int caldera_read(trigger * t, struct gamedata *data)
-{
-    int rb =
-        read_reference(&t->data.v, data, read_building_reference,
-        resolve_building);
-    if (rb == 0 && !t->data.v) {
-        return AT_READ_FAIL;
-    }
-    return AT_READ_OK;
-}
-
-struct trigger_type tt_caldera = {
-    "caldera",
-    NULL,
-    NULL,
-    caldera_handle,
-    caldera_write,
-    caldera_read
-};
-
-#endif
-
-void register_arena(void)
-{
-    at_deprecate("hurting", a_readint);
-    register_function((pf_generic)enter_arena, "enter_arena");
-    register_function((pf_generic)leave_arena, "leave_arena");
-    tt_register(&tt_caldera);
-}
-
-#endif /* def ARENA_MODULE */
diff --git a/src/modules/arena.h b/src/modules/arena.h
deleted file mode 100644
index 64bfc7e87..000000000
--- a/src/modules/arena.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
-Copyright (c) 1998-2015, 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.
-**/
-
-#ifndef ARENA_H
-#define ARENA_H
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#if ARENA_MODULE == 0
-#error "must define ARENA_MODULE to use this module"
-#endif
-    /* exports: */
-    extern struct plane *arena;
-
-    extern void register_arena(void);
-#ifdef ARENA_CREATION
-    extern void create_arena(void);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/src/settings.h b/src/settings.h
index b77e4687a..e7ec39305 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -31,7 +31,6 @@
  * or both. We don't want separate binaries for different games
  */
 #define MUSEUM_MODULE 1
-#define ARENA_MODULE 1
 
 #undef REGIONOWNERS             /* (WIP) region-owner uses HELP_TRAVEL to control entry to region */
 

From 1b93c148065a6353eb648a0223f778a69cf09517 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Mon, 27 Feb 2017 18:50:48 +0100
Subject: [PATCH 21/42] Test E2 items in E2 only. Remove eye of demon (broken
 item).

---
 res/core/common/items.xml  |  16 +-----
 res/core/de/strings.xml    |   6 --
 res/core/en/strings.xml    |   6 --
 res/core/fr/strings.xml    |   6 --
 res/eressea/items.xml      |   4 ++
 res/eressea/strings.xml    |  11 +++-
 scripts/tests/e2/init.lua  |   1 +
 scripts/tests/e2/items.lua | 109 +++++++++++++++++++++++++++++++++++
 scripts/tests/e3/init.lua  |   1 +
 scripts/tests/items.lua    | 115 ++++++++-----------------------------
 src/items.c                |  33 +++++++++--
 src/items/CMakeLists.txt   |   1 -
 src/items/demonseye.c      |  65 ---------------------
 src/items/demonseye.h      |  30 ----------
 src/kernel/item.c          |  29 ----------
 src/modules/gmcmd.c        |   1 -
 16 files changed, 180 insertions(+), 254 deletions(-)
 create mode 100644 scripts/tests/e2/items.lua
 delete mode 100644 src/items/demonseye.c
 delete mode 100644 src/items/demonseye.h

diff --git a/res/core/common/items.xml b/res/core/common/items.xml
index 63a07d88f..0e1505205 100644
--- a/res/core/common/items.xml
+++ b/res/core/common/items.xml
@@ -61,14 +61,8 @@
     <item weight="0" score="6000"/>
   </resource>
 
-  <resource name="aoc" appearance="amulet">
-    <item weight="100" use="yes" />
-  </resource>
-
   <resource name="dreameye">
-    <item weight="100">
-      <function name="use" value="use_tacticcrystal"/>
-    </item>
+    <item weight="100" use="yes" />
   </resource>
 
   <resource name="pegasus">
@@ -105,16 +99,12 @@
 
   <resource name="skillpotion" appearance="vial">
     <!-- gives user one free learning attempt -->
-    <item weight="0">
-      <function name="use" value="use_skillpotion"/>
-    </item>
+    <item weight="0" use="yes" />
   </resource>
 
   <resource name="manacrystal" appearance="amulet">
     <!-- gives user free aura -->
-    <item weight="0">
-      <function name="use" value="use_manacrystal"/>
-    </item>
+    <item weight="0" use="yes" />
   </resource>
 
 <!-- xmas items -->
diff --git a/res/core/de/strings.xml b/res/core/de/strings.xml
index 26e8cc862..46d5605b1 100644
--- a/res/core/de/strings.xml
+++ b/res/core/de/strings.xml
@@ -1458,12 +1458,6 @@
   <string name="aots_p">
     <text locale="de">Amulette des wahren Sehens</text>
   </string>
-  <string name="aoc">
-    <text locale="de">Katzenamulett</text>
-  </string>
-  <string name="aoc_p">
-    <text locale="de">Katzenamulette</text>
-  </string>
   <string name="roi">
     <text locale="de">Ring der Unsichtbarkeit</text>
   </string>
diff --git a/res/core/en/strings.xml b/res/core/en/strings.xml
index 063194c5d..14b2b817d 100644
--- a/res/core/en/strings.xml
+++ b/res/core/en/strings.xml
@@ -444,12 +444,6 @@
   <string name="ao_chastity_p">
     <text locale="en">amulets of chastity</text>
   </string>
-  <string name="aoc">
-    <text locale="en">amulet of the kitten</text>
-  </string>
-  <string name="aoc_p">
-    <text locale="en">amulets of the kitten</text>
-  </string>
   <string name="aod">
     <text locale="en">amulet of darkness</text>
   </string>
diff --git a/res/core/fr/strings.xml b/res/core/fr/strings.xml
index c69bb5b75..13e6beb25 100644
--- a/res/core/fr/strings.xml
+++ b/res/core/fr/strings.xml
@@ -458,12 +458,6 @@
   <string name="ao_chastity_p">
     <text locale="fr">amulettes de chasteté</text>
   </string>
-  <string name="aoc">
-    <text locale="fr">amulette du chaton</text>
-  </string>
-  <string name="aoc_p">
-    <text locale="fr">amulettes du chaton</text>
-  </string>
   <string name="aod">
     <text locale="fr">amulette de ténčbres</text>
   </string>
diff --git a/res/eressea/items.xml b/res/eressea/items.xml
index 007f0277c..a785691fe 100644
--- a/res/eressea/items.xml
+++ b/res/eressea/items.xml
@@ -86,6 +86,10 @@
     <item notlost="yes" cursed="true" weight="0"/>
   </resource>
 
+  <resource name="aoc" appearance="amulet">
+    <item weight="100" use="yes" />
+  </resource>
+
   <resource name="eyeofdragon">
     <!-- the arena gate, for one-time entry -->
     <item weight="0" score="0"/>
diff --git a/res/eressea/strings.xml b/res/eressea/strings.xml
index 4869014c5..8afeabbca 100644
--- a/res/eressea/strings.xml
+++ b/res/eressea/strings.xml
@@ -392,5 +392,14 @@
     <text locale="de">Adamantiumrüstungen</text>
     <text locale="en">adamantium plates</text>
   </string>
-
+    <string name="aoc">
+        <text locale="de">Katzenamulett</text>
+        <text locale="en">amulet of the kitten</text>
+        <text locale="fr">amulette du chaton</text>
+    </string>
+    <string name="aoc_p">
+        <text locale="en">amulets of the kitten</text>
+        <text locale="de">Katzenamulette</text>
+        <text locale="fr">amulettes du chaton</text>
+    </string>
 </strings>
diff --git a/scripts/tests/e2/init.lua b/scripts/tests/e2/init.lua
index 4d916d1a9..5f81933d7 100644
--- a/scripts/tests/e2/init.lua
+++ b/scripts/tests/e2/init.lua
@@ -8,6 +8,7 @@ require 'tests.e2.destroy'
 require 'tests.e2.guard'
 require 'tests.e2.spells'
 require 'tests.e2.stealth'
+require 'tests.e2.items'
 require 'tests.items'
 require 'tests.orders'
 require 'tests.common'
diff --git a/scripts/tests/e2/items.lua b/scripts/tests/e2/items.lua
new file mode 100644
index 000000000..da72bdbbc
--- /dev/null
+++ b/scripts/tests/e2/items.lua
@@ -0,0 +1,109 @@
+require "lunit"
+
+module("tests.items", package.seeall, lunit.testcase )
+
+function setup()
+    eressea.free_game()
+    eressea.settings.set("nmr.timeout", "0")
+    eressea.settings.set("rules.food.flags", "4")
+    eressea.settings.set("rules.ship.storms", "0")
+    eressea.settings.set("rules.encounters", "0")
+    eressea.settings.set("magic.regeneration.enable", "0")
+end
+
+function test_meow()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u:add_item("aoc", 1)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Katzenamulett")
+    process_orders()
+    assert_equal(1, u:get_item("aoc"))
+    assert_equal(1, r:count_msg_type('meow'))
+end
+
+function test_aurapotion50()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u:add_item("aurapotion50", 1)
+    u:set_skill('magic', 10);
+    u.magic = 'gwyrrd'
+    u.aura = 0
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Auratrank")
+    process_orders()
+    assert_equal(0, u:get_item("aurapotion50"))
+    assert_equal(1, f:count_msg_type('aurapotion50'))
+    assert_equal(50, u.aura)
+end
+
+function test_bagpipe()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u:add_item("bagpipeoffear", 1)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Dudelsack")
+    process_orders()
+    assert_equal(1, u:get_item("bagpipeoffear"))
+    assert_equal(1, f:count_msg_type('bagpipeoffear_faction'))
+    assert_equal(1, r:count_msg_type('bagpipeoffear_region'))
+end
+
+function test_speedsail()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u.ship = ship.create(r, "boat")
+    u:add_item("speedsail", 2)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Sonnensegel")
+    process_orders()
+    assert_equal(1, u:get_item("speedsail"))
+    assert_equal(1, f:count_msg_type('use_speedsail'))
+end
+
+function test_foolpotion()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u:add_item("p7", 1)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Dumpfbackenbrot 4242")
+    process_orders()
+    assert_equal(1, u:get_item("p7"))
+    assert_equal(1, f:count_msg_type('feedback_unit_not_found'))
+    local u2 = unit.create(f, r, 1)
+    
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Dumpfbackenbrot " .. itoa36(u2.id))
+    process_orders()
+    assert_equal(1, u:get_item("p7"))
+    assert_equal(1, f:count_msg_type('error64'))
+
+    u:set_skill("stealth", 1);
+    process_orders()
+    assert_equal(0, u:get_item("p7"))
+    assert_equal(1, f:count_msg_type('givedumb'))
+end
+
+function test_snowman()
+    local r = region.create(0, 0, "glacier")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u:add_item("snowman", 1)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Schneemann")
+    process_orders()
+    for u2 in r.units do
+        if u2.id~=u.id then
+            assert_equal("snowman", u2.race)
+            assert_equal(1000, u2.hp)
+            u = nil
+            break
+        end
+    end
+    assert_equal(nil, u)
+end
diff --git a/scripts/tests/e3/init.lua b/scripts/tests/e3/init.lua
index 4655fdcef..95df86c4c 100644
--- a/scripts/tests/e3/init.lua
+++ b/scripts/tests/e3/init.lua
@@ -6,6 +6,7 @@ require 'tests.e3.parser'
 require 'tests.e3.morale'
 require 'tests.orders'
 require 'tests.common'
+require 'tests.items'
 -- require 'tests.report'
 require 'tests.magicbag'
 require 'tests.process'
diff --git a/scripts/tests/items.lua b/scripts/tests/items.lua
index 15d638a63..ff292b4cb 100644
--- a/scripts/tests/items.lua
+++ b/scripts/tests/items.lua
@@ -8,61 +8,35 @@ function setup()
     eressea.settings.set("rules.food.flags", "4")
     eressea.settings.set("rules.ship.storms", "0")
     eressea.settings.set("rules.encounters", "0")
-end
-
-function test_meow()
-    local r = region.create(0, 0, "plain")
-    local f = faction.create("noreply@eressea.de", "human", "de")
-    local u = unit.create(f, r, 1)
-    u:add_item("aoc", 1)
-    u:clear_orders()
-    u:add_order("BENUTZEN 1 Katzenamulett")
-    process_orders()
-    assert_equal(1, u:get_item("aoc"))
-    assert_equal(1, r:count_msg_type('meow'))
-end
-
-function test_aurapotion50()
     eressea.settings.set("magic.regeneration.enable", "0")
+end
+
+function test_dreameye()
     local r = region.create(0, 0, "plain")
     local f = faction.create("noreply@eressea.de", "human", "de")
     local u = unit.create(f, r, 1)
-    u:add_item("aurapotion50", 1)
-    u:set_skill('magic', 10);
-    u.magic = 'gwyrrd'
+    u:add_item("dreameye", 2)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Traumauge")
+    process_orders()
+    assert_equal(1, u:get_item("dreameye"))
+    assert_equal(1, f:count_msg_type('use_tacticcrystal'))
+end
+
+function test_manacrystal()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u:add_item("manacrystal", 2)
+    u:clear_orders()
+    u.magic = "gwyrrd"
+    u:set_skill('magic', 1)
     u.aura = 0
-    u:clear_orders()
-    u:add_order("BENUTZEN 1 Auratrank")
+    u:add_order("BENUTZEN 1 Astralkristall")
     process_orders()
-    assert_equal(0, u:get_item("aurapotion50"))
-    assert_equal(1, f:count_msg_type('aurapotion50'))
-    assert_equal(50, u.aura)
-end
-
-function test_bagpipe()
-    local r = region.create(0, 0, "plain")
-    local f = faction.create("noreply@eressea.de", "human", "de")
-    local u = unit.create(f, r, 1)
-    u:add_item("bagpipeoffear", 1)
-    u:clear_orders()
-    u:add_order("BENUTZEN 1 Dudelsack")
-    process_orders()
-    assert_equal(1, u:get_item("bagpipeoffear"))
-    assert_equal(1, f:count_msg_type('bagpipeoffear_faction'))
-    assert_equal(1, r:count_msg_type('bagpipeoffear_region'))
-end
-
-function test_speedsail()
-    local r = region.create(0, 0, "plain")
-    local f = faction.create("noreply@eressea.de", "human", "de")
-    local u = unit.create(f, r, 1)
-    u.ship = ship.create(r, "boat")
-    u:add_item("speedsail", 2)
-    u:clear_orders()
-    u:add_order("BENUTZEN 1 Sonnensegel")
-    process_orders()
-    assert_equal(1, u:get_item("speedsail"))
-    assert_equal(1, f:count_msg_type('use_speedsail'))
+    assert_equal(1, u:get_item("manacrystal"))
+    assert_equal(25, u.aura)
+    assert_equal(1, f:count_msg_type('manacrystal_use'))
 end
 
 function test_skillpotion()
@@ -141,46 +115,3 @@ function test_bloodpotion_other()
     assert_equal(1, f:count_msg_type('usepotion'))
     assert_equal("smurf", u.race)
 end
-
-function test_foolpotion()
-    local r = region.create(0, 0, "plain")
-    local f = faction.create("noreply@eressea.de", "human", "de")
-    local u = unit.create(f, r, 1)
-    u:add_item("p7", 1)
-    u:clear_orders()
-    u:add_order("BENUTZEN 1 Dumpfbackenbrot 4242")
-    process_orders()
-    assert_equal(1, u:get_item("p7"))
-    assert_equal(1, f:count_msg_type('feedback_unit_not_found'))
-    local u2 = unit.create(f, r, 1)
-    
-    u:clear_orders()
-    u:add_order("BENUTZEN 1 Dumpfbackenbrot " .. itoa36(u2.id))
-    process_orders()
-    assert_equal(1, u:get_item("p7"))
-    assert_equal(1, f:count_msg_type('error64'))
-
-    u:set_skill("stealth", 1);
-    process_orders()
-    assert_equal(0, u:get_item("p7"))
-    assert_equal(1, f:count_msg_type('givedumb'))
-end
-
-function test_snowman()
-    local r = region.create(0, 0, "glacier")
-    local f = faction.create("noreply@eressea.de", "human", "de")
-    local u = unit.create(f, r, 1)
-    u:add_item("snowman", 1)
-    u:clear_orders()
-    u:add_order("BENUTZEN 1 Schneemann")
-    process_orders()
-    for u2 in r.units do
-        if u2.id~=u.id then
-            assert_equal("snowman", u2.race)
-            assert_equal(1000, u2.hp)
-            u = nil
-            break
-        end
-    end
-    assert_equal(nil, u)
-end
diff --git a/src/items.c b/src/items.c
index 197240d3e..ccba7ae7f 100644
--- a/src/items.c
+++ b/src/items.c
@@ -21,8 +21,6 @@
 #include <kernel/spell.h>
 #include <kernel/unit.h>
 
-#include <items/demonseye.h>
-
 /* triggers includes */
 #include <triggers/changerace.h>
 #include <triggers/timeout.h>
@@ -340,11 +338,38 @@ struct order *ord)
     return 0;
 }
 
+/* ------------------------------------------------------------- */
+/* Kann auch von Nichtmagier benutzt werden, modifiziert Taktik fuer diese
+* Runde um -1 - 4 Punkte. */
+static int
+use_tacticcrystal(unit * u, const struct item_type *itype, int amount,
+    struct order *ord)
+{
+    int i;
+    for (i = 0; i != amount; ++i) {
+        int duration = 1;           /* wirkt nur eine Runde */
+        curse *c;
+        float effect;
+        float power = 5;            /* Widerstand gegen Antimagiesprueche, ist in diesem
+                                    Fall egal, da der curse fuer den Kampf gelten soll,
+                                    der vor den Antimagiezaubern passiert */
+
+        effect = (float)(rng_int() % 6 - 1);
+        c = create_curse(u, &u->attribs, ct_find("skillmod"), power,
+            duration, effect, u->number);
+        c->data.i = SK_TACTICS;
+        UNUSED_ARG(ord);
+    }
+    use_pooled(u, itype->rtype, GET_DEFAULT, amount);
+    ADDMSG(&u->faction->msgs, msg_message("use_tacticcrystal",
+        "unit region", u, u->region));
+    return 0;
+}
+
 void register_itemfunctions(void)
 {
-    register_demonseye();
-
     /* have tests: */
+    register_item_use(use_tacticcrystal, "use_dreameye");
     register_item_use(use_studypotion, "use_studypotion");
     register_item_use(use_antimagiccrystal, "use_antimagic");
     register_item_use(use_speedsail, "use_speedsail");
diff --git a/src/items/CMakeLists.txt b/src/items/CMakeLists.txt
index bbf192a66..37777f9e4 100644
--- a/src/items/CMakeLists.txt
+++ b/src/items/CMakeLists.txt
@@ -5,7 +5,6 @@ xerewards.test.c
 )
 
 SET(_FILES
-demonseye.c
 speedsail.c
 weapons.c
 xerewards.c
diff --git a/src/items/demonseye.c b/src/items/demonseye.c
deleted file mode 100644
index 563a16047..000000000
--- a/src/items/demonseye.c
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
-Copyright (c) 1998-2015, 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.
-**/
-
-#include <platform.h>
-#include "demonseye.h"
-
-/* kernel includes */
-#include <kernel/faction.h>
-#include <kernel/item.h>
-#include <kernel/messages.h>
-#include <kernel/plane.h>
-#include <kernel/region.h>
-#include <kernel/unit.h>
-
-/* util includes */
-#include <util/functions.h>
-
-/* libc includes */
-#include <assert.h>
-
-static int
-summon_igjarjuk(struct unit *u, const struct item_type *itype, int amount,
-struct order *ord)
-{
-    struct plane *p = rplane(u->region);
-    UNUSED_ARG(amount);
-    UNUSED_ARG(itype);
-    if (p != NULL) {
-        ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "use_realworld_only", ""));
-        return EUNUSABLE;
-    }
-    else {
-        assert(!"not implemented");
-        return EUNUSABLE;
-    }
-}
-
-static int
-give_igjarjuk(struct unit *src, struct unit *d, const struct item_type *itype,
-int n, struct order *ord)
-{
-    ADDMSG(&src->faction->msgs, msg_feedback(src, ord, "error_giveeye", ""));
-    return 0;
-}
-
-void register_demonseye(void)
-{
-    register_item_use(summon_igjarjuk, "useigjarjuk");
-    register_item_give(give_igjarjuk, "giveigjarjuk");
-}
diff --git a/src/items/demonseye.h b/src/items/demonseye.h
deleted file mode 100644
index 8d878a174..000000000
--- a/src/items/demonseye.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
-Copyright (c) 1998-2015, 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.
-**/
-
-#ifndef H_ITM_DEMONSEYE
-#define H_ITM_DEMONSEYE
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-    void register_demonseye(void);
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/src/kernel/item.c b/src/kernel/item.c
index ecad829ca..adfddf6f4 100644
--- a/src/kernel/item.c
+++ b/src/kernel/item.c
@@ -677,34 +677,6 @@ int set_item(unit * u, const item_type *itype, int value)
 #define FL_ITEM_ANIMAL  (1<<3)  /* ist ein Tier */
 #define FL_ITEM_MOUNT ((1<<4) | FL_ITEM_ANIMAL) /* ist ein Reittier */
 
-/* ------------------------------------------------------------- */
-/* Kann auch von Nichtmagier benutzt werden, modifiziert Taktik fuer diese
- * Runde um -1 - 4 Punkte. */
-static int
-use_tacticcrystal(unit * u, const struct item_type *itype, int amount,
-struct order *ord)
-{
-    int i;
-    for (i = 0; i != amount; ++i) {
-        int duration = 1;           /* wirkt nur eine Runde */
-        curse *c;
-        float effect;
-        float power = 5;            /* Widerstand gegen Antimagiesprueche, ist in diesem
-                                       Fall egal, da der curse fuer den Kampf gelten soll,
-                                       der vor den Antimagiezaubern passiert */
-
-        effect = (float)(rng_int() % 6 - 1);
-        c = create_curse(u, &u->attribs, ct_find("skillmod"), power,
-            duration, effect, u->number);
-        c->data.i = SK_TACTICS;
-        UNUSED_ARG(ord);
-    }
-    use_pooled(u, itype->rtype, GET_DEFAULT, amount);
-    ADDMSG(&u->faction->msgs, msg_message("use_tacticcrystal",
-        "unit region", u, u->region));
-    return 0;
-}
-
 typedef struct t_item {
     const char *name;
     /* [0]: Einzahl fuer eigene; [1]: Mehrzahl fuer eigene;
@@ -1165,7 +1137,6 @@ void register_resources(void)
 
     register_item_use(use_potion_delayed, "use_p2");
     register_item_use(use_warmthpotion, "use_nestwarmth");
-    register_item_use(use_tacticcrystal, "use_tacticcrystal");
     register_item_use(use_mistletoe, "usemistletoe");
     register_item_use(use_magicboost, "usemagicboost");
     register_item_use(use_snowball, "usesnowball");
diff --git a/src/modules/gmcmd.c b/src/modules/gmcmd.c
index 9a4e7ac47..c911e3de4 100644
--- a/src/modules/gmcmd.c
+++ b/src/modules/gmcmd.c
@@ -16,7 +16,6 @@
 #include <kernel/command.h>
 
 /* misc includes */
-#include <items/demonseye.h>
 #include <attributes/key.h>
 #include <triggers/gate.h>
 

From 9d09574d5e10c8ec30e6ae8c564030e872ba0e20 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Mon, 27 Feb 2017 19:35:14 +0100
Subject: [PATCH 22/42] enable process_orders to be done in steps. by checking
 before turn_end(), we can sense temporary attributes and curses on a unit
 before they age away.

---
 res/core/common/items.xml  | 10 +++----
 res/eressea/artrewards.xml |  8 ++----
 scripts/tests/e2/items.lua | 18 ++++++++-----
 scripts/tests/items.lua    | 27 +++++++++++++++++--
 src/bind_unit.c            | 19 ++++++++++++++
 src/bindings.c             | 53 +++++++++++++++-----------------------
 src/items.c                | 27 ++++++++++++++++++-
 src/kernel/item.c          | 24 -----------------
 src/laws.c                 | 49 +++++++++++++++++++++++++++++++++--
 src/laws.h                 |  4 ++-
 src/util/attrib.c          | 16 +++++++++---
 src/util/attrib.h          | 18 ++++++-------
 12 files changed, 180 insertions(+), 93 deletions(-)

diff --git a/res/core/common/items.xml b/res/core/common/items.xml
index 0e1505205..8f2bbabf8 100644
--- a/res/core/common/items.xml
+++ b/res/core/common/items.xml
@@ -110,20 +110,16 @@
 <!-- xmas items -->
   <resource name="mistletoe">
     <!-- Sets the chance of escape in a fight to 100 percent -->
-    <item notlost="yes" weight="0">
-      <function name="use" value="usemistletoe"/>
-    </item>
+    <item notlost="yes" weight="0" use="yes" />
   </resource>
 
   <resource name="speedsail">
-    <item weight="0">
-      <function name="use" value="use_speedsail"/>
-    </item>
+    <item weight="0" use="yes" />
   </resource>
 
 <!-- items -->
   <resource name="antimagic" appearance="amulet">
-	  <item weight="0" score="2000" use="yes" />
+    <item weight="0" score="2000" use="yes" />
   </resource>
 
   <resource name="catapultammo">
diff --git a/res/eressea/artrewards.xml b/res/eressea/artrewards.xml
index 2a3c09950..b18fd5b20 100644
--- a/res/eressea/artrewards.xml
+++ b/res/eressea/artrewards.xml
@@ -3,16 +3,12 @@
 
   <resource name="aurapotion50">
     <function name="change" value="changeitem"/>
-    <item weight="0">
-      <function name="use" value="use_aurapotion50"/>
-    </item>
+    <item weight="0" use="yes" />
   </resource>
 
   <resource name="bagpipeoffear">
     <function name="change" value="changeitem"/>
-    <item weight="0">
-      <function name="use" value="use_bagpipeoffear"/>
-    </item>
+    <item weight="0" use="yes"/>
   </resource>
 
 </resources>
diff --git a/scripts/tests/e2/items.lua b/scripts/tests/e2/items.lua
index da72bdbbc..9bc747650 100644
--- a/scripts/tests/e2/items.lua
+++ b/scripts/tests/e2/items.lua
@@ -1,6 +1,6 @@
 require "lunit"
 
-module("tests.items", package.seeall, lunit.testcase )
+module("tests.e2.items", package.seeall, lunit.testcase )
 
 function setup()
     eressea.free_game()
@@ -18,9 +18,11 @@ function test_meow()
     u:add_item("aoc", 1)
     u:clear_orders()
     u:add_order("BENUTZEN 1 Katzenamulett")
-    process_orders()
+    turn_begin()
+    turn_process()
     assert_equal(1, u:get_item("aoc"))
     assert_equal(1, r:count_msg_type('meow'))
+    turn_end()
 end
 
 function test_aurapotion50()
@@ -33,10 +35,12 @@ function test_aurapotion50()
     u.aura = 0
     u:clear_orders()
     u:add_order("BENUTZEN 1 Auratrank")
-    process_orders()
+    turn_begin()
+    turn_process()
     assert_equal(0, u:get_item("aurapotion50"))
     assert_equal(1, f:count_msg_type('aurapotion50'))
     assert_equal(50, u.aura)
+    turn_end()
 end
 
 function test_bagpipe()
@@ -69,24 +73,26 @@ function test_foolpotion()
     local r = region.create(0, 0, "plain")
     local f = faction.create("noreply@eressea.de", "human", "de")
     local u = unit.create(f, r, 1)
+    turn_begin()
     u:add_item("p7", 1)
     u:clear_orders()
     u:add_order("BENUTZEN 1 Dumpfbackenbrot 4242")
-    process_orders()
+    turn_process()
     assert_equal(1, u:get_item("p7"))
     assert_equal(1, f:count_msg_type('feedback_unit_not_found'))
     local u2 = unit.create(f, r, 1)
     
     u:clear_orders()
     u:add_order("BENUTZEN 1 Dumpfbackenbrot " .. itoa36(u2.id))
-    process_orders()
+    turn_process()
     assert_equal(1, u:get_item("p7"))
     assert_equal(1, f:count_msg_type('error64'))
 
     u:set_skill("stealth", 1);
-    process_orders()
+    turn_process()
     assert_equal(0, u:get_item("p7"))
     assert_equal(1, f:count_msg_type('givedumb'))
+    turn_end()
 end
 
 function test_snowman()
diff --git a/scripts/tests/items.lua b/scripts/tests/items.lua
index ff292b4cb..48f466409 100644
--- a/scripts/tests/items.lua
+++ b/scripts/tests/items.lua
@@ -11,6 +11,22 @@ function setup()
     eressea.settings.set("magic.regeneration.enable", "0")
 end
 
+function test_mistletoe()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u:add_item('mistletoe', 2)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Mistelzweig")
+    process_orders()
+    assert_equal(1, u:get_item('mistletoe'))
+    assert_equal(1, f:count_msg_type('use_item'))
+    u.number = 2
+    process_orders()
+    assert_equal(1, u:get_item('mistletoe'))
+    assert_equal(1, f:count_msg_type('use_singleperson'))
+end
+
 function test_dreameye()
     local r = region.create(0, 0, "plain")
     local f = faction.create("noreply@eressea.de", "human", "de")
@@ -18,9 +34,14 @@ function test_dreameye()
     u:add_item("dreameye", 2)
     u:clear_orders()
     u:add_order("BENUTZEN 1 Traumauge")
-    process_orders()
+    assert_false(u:is_cursed('skillmod'))
+    turn_begin()
+    turn_process()
+    assert_true(u:is_cursed('skillmod'))
     assert_equal(1, u:get_item("dreameye"))
     assert_equal(1, f:count_msg_type('use_tacticcrystal'))
+    turn_end()
+    assert_false(u:is_cursed('skillmod'))
 end
 
 function test_manacrystal()
@@ -33,10 +54,12 @@ function test_manacrystal()
     u:set_skill('magic', 1)
     u.aura = 0
     u:add_order("BENUTZEN 1 Astralkristall")
-    process_orders()
+    turn_begin()
+    turn_process()
     assert_equal(1, u:get_item("manacrystal"))
     assert_equal(25, u.aura)
     assert_equal(1, f:count_msg_type('manacrystal_use'))
+    turn_end()
 end
 
 function test_skillpotion()
diff --git a/src/bind_unit.c b/src/bind_unit.c
index c8ac2b362..0358e1938 100755
--- a/src/bind_unit.c
+++ b/src/bind_unit.c
@@ -26,6 +26,7 @@ without prior permission by the authors of Eressea.
 /*  kernel includes */
 #include <kernel/building.h>
 #include <kernel/config.h>
+#include <kernel/curse.h>
 #include <kernel/faction.h>
 #include <kernel/group.h>
 #include <kernel/item.h>
@@ -770,6 +771,21 @@ static int tolua_unit_get_orders(lua_State * L)
     return 1;
 }
 
+static int tolua_unit_is_cursed(lua_State *L) {
+    unit *self = (unit *)tolua_tousertype(L, 1, 0);
+    const char *name = tolua_tostring(L, 2, 0);
+    lua_pushboolean(L, self->attribs && curse_active(get_curse(self->attribs, ct_find(name))));
+    return 1;
+}
+
+static int tolua_unit_has_attrib(lua_State *L) {
+    unit *self = (unit *)tolua_tousertype(L, 1, 0);
+    const char *name = tolua_tostring(L, 2, 0);
+    attrib * a = a_find(self->attribs, at_find(name));
+    lua_pushboolean(L, a != NULL);
+    return 1;
+}
+
 static int tolua_unit_get_flag(lua_State * L)
 {
     unit *self = (unit *)tolua_tousertype(L, 1, 0);
@@ -956,6 +972,9 @@ void tolua_unit_open(lua_State * L)
             tolua_function(L, TOLUA_CAST "clear_orders", &tolua_unit_clear_orders);
             tolua_variable(L, TOLUA_CAST "orders", &tolua_unit_get_orders, 0);
 
+            tolua_function(L, TOLUA_CAST "is_cursed", &tolua_unit_is_cursed);
+            tolua_function(L, TOLUA_CAST "has_attrib", &tolua_unit_has_attrib);
+
             /*  key-attributes for named flags: */
             tolua_function(L, TOLUA_CAST "set_flag", &tolua_unit_set_flag);
             tolua_function(L, TOLUA_CAST "get_flag", &tolua_unit_get_flag);
diff --git a/src/bindings.c b/src/bindings.c
index 18261a507..1d6270b35 100755
--- a/src/bindings.c
+++ b/src/bindings.c
@@ -480,44 +480,30 @@ static int tolua_write_reports(lua_State * L)
     return 1;
 }
 
-static void reset_game(void)
-{
-    region *r;
-    faction *f;
-    for (r = regions; r; r = r->next) {
-        unit *u;
-        building *b;
-        r->flags &= RF_SAVEMASK;
-        for (u = r->units; u; u = u->next) {
-            u->flags &= UFL_SAVEMASK;
-        }
-        for (b = r->buildings; b; b = b->next) {
-            b->flags &= BLD_SAVEMASK;
-        }
-        if (r->land && r->land->ownership && r->land->ownership->owner) {
-            faction *owner = r->land->ownership->owner;
-            if (owner == get_monsters()) {
-                /* some compat-fix, i believe. */
-                owner = update_owners(r);
-            }
-            if (owner) {
-                fset(r, RF_GUARDED);
-            }
-        }
-    }
-    for (f = factions; f; f = f->next) {
-        f->flags &= FFL_SAVEMASK;
-    }
-}
-
 static int tolua_process_orders(lua_State * L)
 {
-    ++turn;
-    reset_game();
     processorders();
     return 0;
 }
 
+static int tolua_turn_begin(lua_State * L)
+{
+    turn_begin();
+    return 0;
+}
+
+static int tolua_turn_process(lua_State * L)
+{
+    turn_process();
+    return 0;
+}
+
+static int tolua_turn_end(lua_State * L)
+{
+    turn_end();
+    return 0;
+}
+
 static int tolua_write_passwords(lua_State * L)
 {
     int result = writepasswd();
@@ -1063,6 +1049,9 @@ int tolua_bindings_open(lua_State * L, const dictionary *inifile)
         tolua_function(L, TOLUA_CAST "factions", tolua_get_factions);
         tolua_function(L, TOLUA_CAST "regions", tolua_get_regions);
         tolua_function(L, TOLUA_CAST "read_turn", tolua_read_turn);
+        tolua_function(L, TOLUA_CAST "turn_begin", tolua_turn_begin);
+        tolua_function(L, TOLUA_CAST "turn_process", tolua_turn_process);
+        tolua_function(L, TOLUA_CAST "turn_end", tolua_turn_end);
         tolua_function(L, TOLUA_CAST "process_orders", tolua_process_orders);
         tolua_function(L, TOLUA_CAST "init_reports", tolua_init_reports);
         tolua_function(L, TOLUA_CAST "write_reports", tolua_write_reports);
diff --git a/src/items.c b/src/items.c
index ccba7ae7f..192bb111d 100644
--- a/src/items.c
+++ b/src/items.c
@@ -21,6 +21,8 @@
 #include <kernel/spell.h>
 #include <kernel/unit.h>
 
+#include <attributes/fleechance.h>
+
 /* triggers includes */
 #include <triggers/changerace.h>
 #include <triggers/timeout.h>
@@ -347,7 +349,7 @@ use_tacticcrystal(unit * u, const struct item_type *itype, int amount,
 {
     int i;
     for (i = 0; i != amount; ++i) {
-        int duration = 1;           /* wirkt nur eine Runde */
+        int duration = 1;           /* wirkt nur in dieser Runde */
         curse *c;
         float effect;
         float power = 5;            /* Widerstand gegen Antimagiesprueche, ist in diesem
@@ -366,9 +368,32 @@ use_tacticcrystal(unit * u, const struct item_type *itype, int amount,
     return 0;
 }
 
+static int
+use_mistletoe(struct unit *user, const struct item_type *itype, int amount,
+    struct order *ord)
+{
+    int mtoes =
+        get_pooled(user, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK,
+            user->number);
+
+    if (user->number > mtoes) {
+        ADDMSG(&user->faction->msgs, msg_message("use_singleperson",
+            "unit item region command", user, itype->rtype, user->region, ord));
+        return -1;
+    }
+    use_pooled(user, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK,
+        user->number);
+    a_add(&user->attribs, make_fleechance((float)1.0));
+    ADDMSG(&user->faction->msgs,
+        msg_message("use_item", "unit item", user, itype->rtype));
+
+    return 0;
+}
+
 void register_itemfunctions(void)
 {
     /* have tests: */
+    register_item_use(use_mistletoe, "use_mistletoe");
     register_item_use(use_tacticcrystal, "use_dreameye");
     register_item_use(use_studypotion, "use_studypotion");
     register_item_use(use_antimagiccrystal, "use_antimagic");
diff --git a/src/kernel/item.c b/src/kernel/item.c
index adfddf6f4..1692ed706 100644
--- a/src/kernel/item.c
+++ b/src/kernel/item.c
@@ -746,29 +746,6 @@ static int use_warmthpotion(unit *u, const item_type *itype,
     return 0;
 }
 
-#include <attributes/fleechance.h>
-static int
-use_mistletoe(struct unit *user, const struct item_type *itype, int amount,
-struct order *ord)
-{
-    int mtoes =
-        get_pooled(user, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK,
-        user->number);
-
-    if (user->number > mtoes) {
-        ADDMSG(&user->faction->msgs, msg_message("use_singleperson",
-            "unit item region command", user, itype->rtype, user->region, ord));
-        return -1;
-    }
-    use_pooled(user, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK,
-        user->number);
-    a_add(&user->attribs, make_fleechance((float)1.0));
-    ADDMSG(&user->faction->msgs,
-        msg_message("use_item", "unit item", user, itype->rtype));
-
-    return 0;
-}
-
 static int
 use_magicboost(struct unit *user, const struct item_type *itype, int amount,
 struct order *ord)
@@ -1137,7 +1114,6 @@ void register_resources(void)
 
     register_item_use(use_potion_delayed, "use_p2");
     register_item_use(use_warmthpotion, "use_nestwarmth");
-    register_item_use(use_mistletoe, "usemistletoe");
     register_item_use(use_magicboost, "usemagicboost");
     register_item_use(use_snowball, "usesnowball");
 
diff --git a/src/laws.c b/src/laws.c
index 0d0e53110..cc10baeb5 100644
--- a/src/laws.c
+++ b/src/laws.c
@@ -4186,16 +4186,54 @@ void init_processor(void)
     }
 }
 
-void processorders(void)
+static void reset_game(void)
+{
+    region *r;
+    faction *f;
+    for (r = regions; r; r = r->next) {
+        unit *u;
+        building *b;
+        r->flags &= RF_SAVEMASK;
+        for (u = r->units; u; u = u->next) {
+            u->flags &= UFL_SAVEMASK;
+        }
+        for (b = r->buildings; b; b = b->next) {
+            b->flags &= BLD_SAVEMASK;
+        }
+        if (r->land && r->land->ownership && r->land->ownership->owner) {
+            faction *owner = r->land->ownership->owner;
+            if (owner == get_monsters()) {
+                /* some compat-fix, i believe. */
+                owner = update_owners(r);
+            }
+            if (owner) {
+                fset(r, RF_GUARDED);
+            }
+        }
+    }
+    for (f = factions; f; f = f->next) {
+        f->flags &= FFL_SAVEMASK;
+    }
+}
+
+void turn_begin(void)
+{
+    ++turn;
+    reset_game();
+}
+
+void turn_process(void)
 {
     init_processor();
     process();
-    /*************************************************/
 
     if (config_get_int("modules.markets", 0)) {
         do_markets();
     }
+}
 
+void turn_end(void)
+{
     log_info(" - Attribute altern");
     ageing();
     remove_empty_units();
@@ -4210,6 +4248,13 @@ void processorders(void)
     update_spells();
 }
 
+void processorders(void)
+{
+    turn_begin();
+    turn_process();
+    turn_end();
+}
+
 void update_subscriptions(void)
 {
     FILE *F;
diff --git a/src/laws.h b/src/laws.h
index f05c11ab4..090d2b978 100755
--- a/src/laws.h
+++ b/src/laws.h
@@ -53,8 +53,10 @@ extern "C" {
     int enter_building(struct unit *u, struct order *ord, int id, bool report);
     int enter_ship(struct unit *u, struct order *ord, int id, bool report);
 
-    /* eressea-specific. put somewhere else, please. */
     void processorders(void);
+    void turn_begin(void);
+    void turn_process(void);
+    void turn_end(void);
 
     void new_units(void);
     void defaultorders(void);
diff --git a/src/util/attrib.c b/src/util/attrib.c
index fda143168..993f634d2 100644
--- a/src/util/attrib.c
+++ b/src/util/attrib.c
@@ -179,7 +179,17 @@ void at_register(attrib_type * at)
     at_hash[at->hashkey % MAXATHASH] = at;
 }
 
-static attrib_type *at_find(unsigned int hk)
+struct attrib_type *at_find(const char *name) {
+    attrib_type *find;
+    unsigned int hash = __at_hashkey(name);
+    find = at_hash[hash % MAXATHASH];
+    while (find && hash != find->hashkey) {
+        find = find->nexthash;
+    }
+    return find;
+}
+
+static attrib_type *at_find_key(unsigned int hk)
 {
     const char *translate[3][2] = {
         { "zielregion", "targetregion" },     /* remapping: from 'zielregion, heute targetregion */
@@ -193,7 +203,7 @@ static attrib_type *at_find(unsigned int hk)
         int i = 0;
         while (translate[i][0]) {
             if (__at_hashkey(translate[i][0]) == hk)
-                return at_find(__at_hashkey(translate[i][1]));
+                return at_find_key(__at_hashkey(translate[i][1]));
             ++i;
         }
     }
@@ -408,7 +418,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;
-    attrib_type *at = at_find(key);
+    attrib_type *at = at_find_key(key);
     attrib * na = 0;
 
     if (at) {
diff --git a/src/util/attrib.h b/src/util/attrib.h
index 601a6f32a..6043a960f 100644
--- a/src/util/attrib.h
+++ b/src/util/attrib.h
@@ -65,19 +65,19 @@ extern "C" {
         unsigned int hashkey;
     } attrib_type;
 
-    extern void at_register(attrib_type * at);
-    extern void at_deprecate(const char * name, int(*reader)(attrib *, void *, struct gamedata *));
+    void at_register(attrib_type * at);
+    void at_deprecate(const char * name, int(*reader)(attrib *, void *, struct gamedata *));
+    struct attrib_type *at_find(const char *name);
 
     void write_attribs(struct storage *store, struct attrib *alist, const void *owner);
     int read_attribs(struct gamedata *store, struct attrib **alist, void *owner);
 
-    extern attrib *a_select(attrib * a, const void *data,
-        bool(*compare) (const attrib *, const void *));
-    extern attrib *a_find(attrib * a, const attrib_type * at);
-    extern attrib *a_add(attrib ** pa, attrib * at);
-    extern int a_remove(attrib ** pa, attrib * at);
-    extern void a_removeall(attrib ** a, const attrib_type * at);
-    extern attrib *a_new(const attrib_type * at);
+    attrib *a_select(attrib * a, const void *data, bool(*compare) (const attrib *, const void *));
+    attrib *a_find(attrib * a, const attrib_type * at);
+    attrib *a_add(attrib ** pa, attrib * at);
+    int a_remove(attrib ** pa, attrib * at);
+    void a_removeall(attrib ** a, const attrib_type * at);
+    attrib *a_new(const attrib_type * at);
     int a_age(attrib ** attribs, void *owner);
 
     int a_read_orig(struct gamedata *data, attrib ** attribs, void *owner);

From 462d0118e30f13f4b9b936c32abd2b32977ea4c2 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Mon, 27 Feb 2017 20:16:50 +0100
Subject: [PATCH 23/42] better testing for some items.

---
 scripts/tests/e2/items.lua  | 16 +++++++++++--
 scripts/tests/items.lua     | 45 ++++++++++++++++++++++++++++++-------
 src/attributes/attributes.c |  3 +++
 src/attributes/fleechance.c |  5 -----
 src/attributes/fleechance.h |  1 -
 src/bind_region.c           | 24 ++++++++++++++++++++
 src/bind_ship.c             | 27 ++++++++++++++++++++++
 src/bind_unit.c             | 16 ++++++++-----
 src/items.c                 |  7 +++---
 src/util/attrib.c           | 16 +++++--------
 10 files changed, 125 insertions(+), 35 deletions(-)

diff --git a/scripts/tests/e2/items.lua b/scripts/tests/e2/items.lua
index 9bc747650..93c88790f 100644
--- a/scripts/tests/e2/items.lua
+++ b/scripts/tests/e2/items.lua
@@ -47,26 +47,38 @@ function test_bagpipe()
     local r = region.create(0, 0, "plain")
     local f = faction.create("noreply@eressea.de", "human", "de")
     local u = unit.create(f, r, 1)
+    turn_begin()
     u:add_item("bagpipeoffear", 1)
     u:clear_orders()
     u:add_order("BENUTZEN 1 Dudelsack")
-    process_orders()
+    assert_equal(nil, r:get_curse('depression'))
+    turn_process()
+    assert_equal(0, r:get_curse('depression'))
     assert_equal(1, u:get_item("bagpipeoffear"))
     assert_equal(1, f:count_msg_type('bagpipeoffear_faction'))
     assert_equal(1, r:count_msg_type('bagpipeoffear_region'))
+    turn_end()
+    -- duration is variable, but at least 4
+    assert_equal(0, r:get_curse('depression'))
 end
 
 function test_speedsail()
     local r = region.create(0, 0, "plain")
     local f = faction.create("noreply@eressea.de", "human", "de")
     local u = unit.create(f, r, 1)
+    
+    turn_begin()
     u.ship = ship.create(r, "boat")
     u:add_item("speedsail", 2)
     u:clear_orders()
     u:add_order("BENUTZEN 1 Sonnensegel")
-    process_orders()
+    assert_equal(nil, u.ship:get_curse('shipspeed'))
+    turn_process()
+    assert_equal(1, u.ship:get_curse('shipspeed'))
     assert_equal(1, u:get_item("speedsail"))
     assert_equal(1, f:count_msg_type('use_speedsail'))
+    turn_end()
+    assert_equal(1, u.ship:get_curse('shipspeed')) -- effect stays forever
 end
 
 function test_foolpotion()
diff --git a/scripts/tests/items.lua b/scripts/tests/items.lua
index 48f466409..f82aa9e7b 100644
--- a/scripts/tests/items.lua
+++ b/scripts/tests/items.lua
@@ -11,20 +11,37 @@ function setup()
     eressea.settings.set("magic.regeneration.enable", "0")
 end
 
-function test_mistletoe()
+function test_mistletoe_okay()
     local r = region.create(0, 0, "plain")
     local f = faction.create("noreply@eressea.de", "human", "de")
     local u = unit.create(f, r, 1)
+    turn_begin()
     u:add_item('mistletoe', 2)
     u:clear_orders()
     u:add_order("BENUTZEN 1 Mistelzweig")
-    process_orders()
+    assert_false(u:has_attrib('fleechance'))
+    turn_process()
+    assert_true(u:has_attrib('fleechance'))
     assert_equal(1, u:get_item('mistletoe'))
     assert_equal(1, f:count_msg_type('use_item'))
+    turn_end()
+end
+
+function test_mistletoe_fail()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    turn_begin()
+    u:add_item('mistletoe', 1)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Mistelzweig")
+    assert_false(u:has_attrib('fleechance'))
     u.number = 2
-    process_orders()
+    turn_process()
+    assert_false(u:has_attrib('fleechance'))
     assert_equal(1, u:get_item('mistletoe'))
     assert_equal(1, f:count_msg_type('use_singleperson'))
+    turn_end()
 end
 
 function test_dreameye()
@@ -34,14 +51,14 @@ function test_dreameye()
     u:add_item("dreameye", 2)
     u:clear_orders()
     u:add_order("BENUTZEN 1 Traumauge")
-    assert_false(u:is_cursed('skillmod'))
+    assert_nil(u:get_curse('skillmod'))
     turn_begin()
     turn_process()
-    assert_true(u:is_cursed('skillmod'))
+    assert_not_nil(u:get_curse('skillmod'))
     assert_equal(1, u:get_item("dreameye"))
     assert_equal(1, f:count_msg_type('use_tacticcrystal'))
     turn_end()
-    assert_false(u:is_cursed('skillmod'))
+    assert_equal(nil, u:get_curse('skillmod'))
 end
 
 function test_manacrystal()
@@ -78,24 +95,36 @@ function test_studypotion()
     local r = region.create(0, 0, "plain")
     local f = faction.create("noreply@eressea.de", "human", "de")
     local u = unit.create(f, r, 1)
+    turn_begin()
     u:add_item("studypotion", 2)
     u:clear_orders()
     u:add_order("LERNE Unterhaltung")
     u:add_order("BENUTZEN 1 Lerntrank")
-    process_orders()
+    turn_process()
+    -- cannot sense the "learning" attribute, because study_cmd
+    -- removes it during processing :(
     assert_equal(1, u:get_item("studypotion"))
+    turn_end()
 end
 
 function test_antimagic()
     local r = region.create(0, 0, "plain")
     local f = faction.create("noreply@eressea.de", "human", "de")
     local u = unit.create(f, r, 1)
+
+    turn_begin()
     u:add_item("antimagic", 2)
     u:clear_orders()
     u:add_order("BENUTZEN 1 Antimagiekristall")
-    process_orders()
+    assert_equal(nil, r:get_curse('antimagiczone'))
+    turn_process()
+    assert_equal(5, r:get_curse('antimagiczone'))
     assert_equal(1, r:count_msg_type('use_antimagiccrystal'))
     assert_equal(1, u:get_item("antimagic"))
+    turn_end()
+    assert_equal(5, r:get_curse('antimagiczone')) -- haelt zwei wochen
+    turn_end() -- hack: age the curse again
+    assert_equal(nil, r:get_curse('antimagiczone'))
 end
 
 function test_ointment()
diff --git a/src/attributes/attributes.c b/src/attributes/attributes.c
index ae6869df8..0fe42a92e 100644
--- a/src/attributes/attributes.c
+++ b/src/attributes/attributes.c
@@ -25,6 +25,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 /* attributes includes */
 #include "follow.h"
+#include "fleechance.h"
 #include "hate.h"
 #include "iceberg.h"
 #include "key.h"
@@ -58,6 +59,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 #include <storage.h>
 
+
 attrib_type at_unitdissolve = {
     "unitdissolve", NULL, NULL, NULL, a_writechars, a_readchars
 };
@@ -92,6 +94,7 @@ void register_attributes(void)
     at_register(&at_woodcount);
 
     /* neue UNIT-Attribute */
+    at_register(&at_fleechance);
     at_register(&at_siege);
     at_register(&at_effect);
     at_register(&at_private);
diff --git a/src/attributes/fleechance.c b/src/attributes/fleechance.c
index afba2d301..b8012fba5 100644
--- a/src/attributes/fleechance.c
+++ b/src/attributes/fleechance.c
@@ -38,8 +38,3 @@ attrib *make_fleechance(float fleechance)
     a->data.flt = fleechance;
     return a;
 }
-
-void init_fleechance(void)
-{
-    at_register(&at_fleechance);
-}
diff --git a/src/attributes/fleechance.h b/src/attributes/fleechance.h
index b2f75671c..d5817b8b7 100644
--- a/src/attributes/fleechance.h
+++ b/src/attributes/fleechance.h
@@ -25,7 +25,6 @@ extern "C" {
     extern struct attrib_type at_fleechance;
 
     struct attrib *make_fleechance(float fleechance);
-    void init_fleechance(void);
 
 #ifdef __cplusplus
 }
diff --git a/src/bind_region.c b/src/bind_region.c
index 05c0e3842..fdab4a10f 100644
--- a/src/bind_region.c
+++ b/src/bind_region.c
@@ -19,6 +19,7 @@ without prior permission by the authors of Eressea.
 #include "chaos.h"
 
 #include <kernel/config.h>
+#include <kernel/curse.h>
 #include <kernel/region.h>
 #include <kernel/resources.h>
 #include <kernel/unit.h>
@@ -670,6 +671,27 @@ static int tolua_distance(lua_State * L)
     return 1;
 }
 
+static int tolua_region_get_curse(lua_State *L) {
+    region *self = (region *)tolua_tousertype(L, 1, 0);
+    const char *name = tolua_tostring(L, 2, 0);
+    if (self->attribs) {
+        curse * c = get_curse(self->attribs, ct_find(name));
+        if (c) {
+            lua_pushnumber(L, curse_geteffect(c));
+            return 1;
+        }
+    }
+    return 0;
+}
+
+static int tolua_region_has_attrib(lua_State *L) {
+    region *self = (region *)tolua_tousertype(L, 1, 0);
+    const char *name = tolua_tostring(L, 2, 0);
+    attrib * a = a_find(self->attribs, at_find(name));
+    lua_pushboolean(L, a != NULL);
+    return 1;
+}
+
 void tolua_region_open(lua_State * L)
 {
     /* register user types */
@@ -691,6 +713,8 @@ void tolua_region_open(lua_State * L)
 
             tolua_function(L, TOLUA_CAST "count_msg_type", tolua_region_count_msg_type);
 
+            tolua_function(L, TOLUA_CAST "get_curse", &tolua_region_get_curse);
+            tolua_function(L, TOLUA_CAST "has_attrib", &tolua_region_has_attrib);
             /* flags */
             tolua_variable(L, TOLUA_CAST "blocked", tolua_region_get_blocked, tolua_region_set_blocked);
 
diff --git a/src/bind_ship.c b/src/bind_ship.c
index 515fa8de3..c5e0735ed 100644
--- a/src/bind_ship.c
+++ b/src/bind_ship.c
@@ -16,11 +16,13 @@ without prior permission by the authors of Eressea.
 
 #include "move.h"
 
+#include <kernel/curse.h>
 #include <kernel/region.h>
 #include <kernel/unit.h>
 #include <kernel/ship.h>
 #include <kernel/build.h>
 
+#include <util/attrib.h>
 #include <util/language.h>
 #include <util/log.h>
 
@@ -198,6 +200,27 @@ static int tolua_ship_set_damage(lua_State * L)
     return 0;
 }
 
+static int tolua_ship_get_curse(lua_State *L) {
+    ship *self = (ship *)tolua_tousertype(L, 1, 0);
+    const char *name = tolua_tostring(L, 2, 0);
+    if (self->attribs) {
+        curse * c = get_curse(self->attribs, ct_find(name));
+        if (c) {
+            lua_pushnumber(L, curse_geteffect(c));
+            return 1;
+        }
+    }
+    return 0;
+}
+
+static int tolua_ship_has_attrib(lua_State *L) {
+    ship *self = (ship *)tolua_tousertype(L, 1, 0);
+    const char *name = tolua_tostring(L, 2, 0);
+    attrib * a = a_find(self->attribs, at_find(name));
+    lua_pushboolean(L, a != NULL);
+    return 1;
+}
+
 void tolua_ship_open(lua_State * L)
 {
     /* register user types */
@@ -225,6 +248,10 @@ void tolua_ship_open(lua_State * L)
             tolua_variable(L, TOLUA_CAST "type", tolua_ship_get_type, 0);
             tolua_variable(L, TOLUA_CAST "damage", tolua_ship_get_damage,
                 tolua_ship_set_damage);
+
+            tolua_function(L, TOLUA_CAST "get_curse", &tolua_ship_get_curse);
+            tolua_function(L, TOLUA_CAST "has_attrib", &tolua_ship_has_attrib);
+
             tolua_function(L, TOLUA_CAST "create", tolua_ship_create);
         }
         tolua_endmodule(L);
diff --git a/src/bind_unit.c b/src/bind_unit.c
index 0358e1938..8247e13d5 100755
--- a/src/bind_unit.c
+++ b/src/bind_unit.c
@@ -771,17 +771,23 @@ static int tolua_unit_get_orders(lua_State * L)
     return 1;
 }
 
-static int tolua_unit_is_cursed(lua_State *L) {
+static int tolua_unit_get_curse(lua_State *L) {
     unit *self = (unit *)tolua_tousertype(L, 1, 0);
     const char *name = tolua_tostring(L, 2, 0);
-    lua_pushboolean(L, self->attribs && curse_active(get_curse(self->attribs, ct_find(name))));
-    return 1;
+    if (self->attribs) {
+        curse * c = get_curse(self->attribs, ct_find(name));
+        if (c) {
+            lua_pushnumber(L, curse_geteffect(c));
+            return 1;
+        }
+    }
+    return 0;
 }
 
 static int tolua_unit_has_attrib(lua_State *L) {
     unit *self = (unit *)tolua_tousertype(L, 1, 0);
     const char *name = tolua_tostring(L, 2, 0);
-    attrib * a = a_find(self->attribs, at_find(name));
+    attrib * a = self->attribs ? a_find(self->attribs, at_find(name)) : NULL;
     lua_pushboolean(L, a != NULL);
     return 1;
 }
@@ -972,7 +978,7 @@ void tolua_unit_open(lua_State * L)
             tolua_function(L, TOLUA_CAST "clear_orders", &tolua_unit_clear_orders);
             tolua_variable(L, TOLUA_CAST "orders", &tolua_unit_get_orders, 0);
 
-            tolua_function(L, TOLUA_CAST "is_cursed", &tolua_unit_is_cursed);
+            tolua_function(L, TOLUA_CAST "get_curse", &tolua_unit_get_curse);
             tolua_function(L, TOLUA_CAST "has_attrib", &tolua_unit_has_attrib);
 
             /*  key-attributes for named flags: */
diff --git a/src/items.c b/src/items.c
index 192bb111d..f8739cb73 100644
--- a/src/items.c
+++ b/src/items.c
@@ -43,7 +43,7 @@ static int
 use_studypotion(struct unit *u, const struct item_type *itype, int amount,
 struct order *ord)
 {
-    if (init_order(u->thisorder) == K_STUDY) {
+    if (u->thisorder && init_order(u->thisorder) == K_STUDY) {
         char token[128];
         skill_t sk = NOSKILL;
         skill *sv = 0;
@@ -97,9 +97,8 @@ struct order *ord)
     }
 
     effect = SPEEDSAIL_EFFECT;
-    c =
-        create_curse(u, &sh->attribs, ct_find("shipspeedup"), 20, INT_MAX, effect,
-        0);
+    c = create_curse(u, &sh->attribs, ct_find("shipspeedup"), 20, INT_MAX, 
+        effect, 0);
     c_setflag(c, CURSE_NOAGE);
 
     ADDMSG(&u->faction->msgs, msg_message("use_speedsail", "unit speed", u,
diff --git a/src/util/attrib.c b/src/util/attrib.c
index 993f634d2..b3b9c0a6e 100644
--- a/src/util/attrib.c
+++ b/src/util/attrib.c
@@ -179,16 +179,6 @@ void at_register(attrib_type * at)
     at_hash[at->hashkey % MAXATHASH] = at;
 }
 
-struct attrib_type *at_find(const char *name) {
-    attrib_type *find;
-    unsigned int hash = __at_hashkey(name);
-    find = at_hash[hash % MAXATHASH];
-    while (find && hash != find->hashkey) {
-        find = find->nexthash;
-    }
-    return find;
-}
-
 static attrib_type *at_find_key(unsigned int hk)
 {
     const char *translate[3][2] = {
@@ -210,6 +200,11 @@ static attrib_type *at_find_key(unsigned int hk)
     return find;
 }
 
+struct attrib_type *at_find(const char *name) {
+    unsigned int hash = __at_hashkey(name);
+    return at_find_key(hash);
+}
+
 attrib *a_select(attrib * a, const void *data,
     bool(*compare) (const attrib *, const void *))
 {
@@ -220,6 +215,7 @@ attrib *a_select(attrib * a, const void *data,
 
 attrib *a_find(attrib * a, const attrib_type * at)
 {
+    assert(at);
     while (a && a->type != at)
         a = a->nexttype;
     return a;

From 9e239c88f486218739d5052aafde7b2a43407e69 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Mon, 27 Feb 2017 20:26:48 +0100
Subject: [PATCH 24/42] testing insect warmth potion

---
 scripts/tests/e2/items.lua | 32 ++++++++++++++++++++++++++++++++
 scripts/tests/items.lua    |  2 +-
 src/items.c                | 21 +++++++++++++++++++++
 src/kernel/item.c          | 29 -----------------------------
 4 files changed, 54 insertions(+), 30 deletions(-)

diff --git a/scripts/tests/e2/items.lua b/scripts/tests/e2/items.lua
index 93c88790f..24f27f4b2 100644
--- a/scripts/tests/e2/items.lua
+++ b/scripts/tests/e2/items.lua
@@ -11,6 +11,38 @@ function setup()
     eressea.settings.set("magic.regeneration.enable", "0")
 end
 
+function test_nestwarmth_insect()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "insect", "de")
+    local u = unit.create(f, r, 1)
+    local flags = u.flags
+    u:add_item("nestwarmth", 2)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Nestwaerme")
+    turn_begin()
+    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'))
+    turn_end()
+end
+
+function test_nestwarmth_other()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    local flags = u.flags
+    u:add_item("nestwarmth", 2)
+    u:clear_orders()
+    u:add_order("BENUTZEN 1 Nestwaerme")
+    turn_begin()
+    turn_process()
+    assert_equal(flags, u.flags) -- nothing happens
+    assert_equal(2, u:get_item("nestwarmth"))
+    assert_equal(1, f:count_msg_type('error163'))
+    turn_end()
+end
+
 function test_meow()
     local r = region.create(0, 0, "plain")
     local f = faction.create("noreply@eressea.de", "human", "de")
diff --git a/scripts/tests/items.lua b/scripts/tests/items.lua
index f82aa9e7b..aad52c2b2 100644
--- a/scripts/tests/items.lua
+++ b/scripts/tests/items.lua
@@ -58,7 +58,7 @@ function test_dreameye()
     assert_equal(1, u:get_item("dreameye"))
     assert_equal(1, f:count_msg_type('use_tacticcrystal'))
     turn_end()
-    assert_equal(nil, u:get_curse('skillmod'))
+    assert_nil(u:get_curse('skillmod'))
 end
 
 function test_manacrystal()
diff --git a/src/items.c b/src/items.c
index f8739cb73..51a4b2970 100644
--- a/src/items.c
+++ b/src/items.c
@@ -389,6 +389,26 @@ use_mistletoe(struct unit *user, const struct item_type *itype, int amount,
     return 0;
 }
 
+static int use_warmthpotion(unit *u, const item_type *itype,
+    int amount, struct order *ord)
+{
+    if (u->faction->race == get_race(RC_INSECT)) {
+        u->flags |= UFL_WARMTH;
+    }
+    else {
+        /* nur fuer insekten: */
+        cmistake(u, ord, 163, MSG_EVENT);
+        return ECUSTOM;
+    }
+    use_pooled(u, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK,
+        amount);
+    usetpotionuse(u, itype->rtype->ptype);
+
+    ADDMSG(&u->faction->msgs, msg_message("usepotion",
+        "unit potion", u, itype->rtype));
+    return 0;
+}
+
 void register_itemfunctions(void)
 {
     /* have tests: */
@@ -403,4 +423,5 @@ void register_itemfunctions(void)
     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_warmthpotion, "use_nestwarmth");
 }
diff --git a/src/kernel/item.c b/src/kernel/item.c
index 1692ed706..8d5224d79 100644
--- a/src/kernel/item.c
+++ b/src/kernel/item.c
@@ -726,26 +726,6 @@ struct order *), const char *name)
     register_function((pf_generic)foo, name);
 }
 
-static int use_warmthpotion(unit *u, const item_type *itype,
-    int amount, struct order *ord)
-{
-    if (u->faction->race == get_race(RC_INSECT)) {
-        fset(u, UFL_WARMTH);
-    }
-    else {
-        /* nur fuer insekten: */
-        cmistake(u, ord, 163, MSG_EVENT);
-        return ECUSTOM;
-    }
-    use_pooled(u, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK,
-        amount);
-    usetpotionuse(u, itype->rtype->ptype);
-
-    ADDMSG(&u->faction->msgs, msg_message("usepotion",
-        "unit potion", u, itype->rtype));
-    return 0;
-}
-
 static int
 use_magicboost(struct unit *user, const struct item_type *itype, int amount,
 struct order *ord)
@@ -775,13 +755,6 @@ struct order *ord)
     return 0;
 }
 
-static int
-use_snowball(struct unit *user, const struct item_type *itype, int amount,
-struct order *ord)
-{
-    return 0;
-}
-
 static void init_oldpotions(void)
 {
     const char *potionnames[MAX_POTIONS] = {
@@ -1113,9 +1086,7 @@ void register_resources(void)
     register_function((pf_generic)res_changeaura, "changeaura");
 
     register_item_use(use_potion_delayed, "use_p2");
-    register_item_use(use_warmthpotion, "use_nestwarmth");
     register_item_use(use_magicboost, "usemagicboost");
-    register_item_use(use_snowball, "usesnowball");
 
     register_item_give(give_horses, "givehorses");
 }

From 49d8a03eca1e5752fc9c7546e53a8b9376653d75 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Mon, 27 Feb 2017 20:41:05 +0100
Subject: [PATCH 25/42] remote the item_type.use funpointer.

---
 res/eressea/items.xml  | 16 ++++------------
 src/helpers.c          |  3 ---
 src/kernel/item.h      |  4 ----
 src/kernel/xmlreader.c |  7 -------
 src/modules/museum.c   |  5 +++--
 5 files changed, 7 insertions(+), 28 deletions(-)

diff --git a/res/eressea/items.xml b/res/eressea/items.xml
index a785691fe..3f5cc3d17 100644
--- a/res/eressea/items.xml
+++ b/res/eressea/items.xml
@@ -54,30 +54,22 @@
 <!-- museum items -->
   <resource name="questkey1" appearance="key">
     <!-- Key for an old quest. placeholder item -->
-    <item notlost="yes" weight="0" >
-      <function name="use" value="use_museumkey"/>
-    </item>
+    <item notlost="yes" weight="0" use="yes" />
   </resource>
 
   <resource name="questkey2" appearance="key">
     <!-- Key for an old quest. placeholder item -->
-    <item notlost="yes" weight="0" >
-      <function name="use" value="use_museumkey"/>
-    </item>
+    <item notlost="yes" weight="0" use="yes" />
   </resource>
 
   <resource name="museumexitticket">
     <!-- you get your stuff back when leaving the museum -->
-    <item notlost="yes" weight="0">
-      <function name="use" value="use_museumexitticket"/>
-    </item>
+    <item notlost="yes" weight="0" use="yes" />
   </resource>
 
   <resource name="museumticket">
     <!-- you get your stuff back when leaving the museum -->
-    <item weight="0">
-      <function name="use" value="use_museumticket"/>
-    </item>
+    <item weight="0" use="yes" />
   </resource>
 
 <!-- gimmicks, etc. -->
diff --git a/src/helpers.c b/src/helpers.c
index 5124ca0ae..e8e6521c4 100644
--- a/src/helpers.c
+++ b/src/helpers.c
@@ -529,9 +529,6 @@ use_item_lua(unit *u, const item_type *itype, int amount, struct order *ord)
     } else {
         log_error("no such callout: %s", fname);
     }
-    if (itype->use) {
-        return itype->use(u, itype, amount, ord);
-    }
     log_error("use(%s) calling '%s': not a function.\n", unitname(u), fname);
     lua_pop(L, 1);
 
diff --git a/src/kernel/item.h b/src/kernel/item.h
index b0bc10cd8..14196ab37 100644
--- a/src/kernel/item.h
+++ b/src/kernel/item.h
@@ -126,8 +126,6 @@ extern "C" {
         /* --- functions --- */
         bool(*canuse) (const struct unit * user,
             const struct item_type * itype);
-        int(*use) (struct unit * user, const struct item_type * itype, int amount,
-        struct order * ord);
         int(*give) (struct unit * src, struct unit * dest,
             const struct item_type * itm, int number, struct order * ord);
         int score;
@@ -310,8 +308,6 @@ extern "C" {
         const struct item_type *, int, struct order *), const char *name);
     void register_item_use(int(*foo) (struct unit *,
         const struct item_type *, int, struct order *), const char *name);
-    void register_item_useonother(int(*foo) (struct unit *, int,
-        const struct item_type *, int, struct order *), const char *name);
 
     void free_resources(void);
 
diff --git a/src/kernel/xmlreader.c b/src/kernel/xmlreader.c
index 69e9c22d0..af0a9dd23 100644
--- a/src/kernel/xmlreader.c
+++ b/src/kernel/xmlreader.c
@@ -854,13 +854,6 @@ static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype)
                 (int(*)(struct unit *, struct unit *, const struct item_type *, int,
             struct order *))fun;
         }
-        else if (strcmp((const char *)propValue, "use") == 0) {
-            log_error("%s has a use function", rtype->_name);
-            itype->flags |= ITF_CANUSE;
-            itype->use =
-                (int(*)(struct unit *, const struct item_type *, int,
-            struct order *))fun;
-        }
         else if (strcmp((const char *)propValue, "canuse") == 0) {
             itype->canuse =
                 (bool(*)(const struct unit *, const struct item_type *))fun;
diff --git a/src/modules/museum.c b/src/modules/museum.c
index af37019d0..1eee3529f 100644
--- a/src/modules/museum.c
+++ b/src/modules/museum.c
@@ -497,9 +497,10 @@ void register_museum(void)
     at_register(&at_museumgivebackcookie);
     at_register(&at_museumgiveback);
 
-    register_item_use(use_museumticket, "use_museumticket");
-    register_item_use(use_museumkey, "use_museumkey");
     register_item_use(use_museumexitticket, "use_museumexitticket");
+    register_item_use(use_museumticket, "use_museumticket");
+    register_item_use(use_museumkey, "use_questkey1");
+    register_item_use(use_museumkey, "use_questkey2");
 }
 
 #endif

From 69e28034b458ad1afd34cb51e49ec088710a111a Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Mon, 27 Feb 2017 21:00:15 +0100
Subject: [PATCH 26/42] Kill unused "magicboost" function. I have no idea why
 this exists.

---
 src/items.c       |  3 +++
 src/kernel/item.c | 32 --------------------------------
 2 files changed, 3 insertions(+), 32 deletions(-)

diff --git a/src/items.c b/src/items.c
index 51a4b2970..78f6e3188 100644
--- a/src/items.c
+++ b/src/items.c
@@ -424,4 +424,7 @@ void register_itemfunctions(void)
     register_item_use(use_bloodpotion, "use_peasantblood");
     register_item_use(use_healingpotion, "use_ointment");
     register_item_use(use_warmthpotion, "use_nestwarmth");
+
+    /* ungetestet: Wasser des Lebens */
+    register_item_use(use_potion_delayed, "use_p2");
 }
diff --git a/src/kernel/item.c b/src/kernel/item.c
index 8d5224d79..6bd97e6dd 100644
--- a/src/kernel/item.c
+++ b/src/kernel/item.c
@@ -726,35 +726,6 @@ struct order *), const char *name)
     register_function((pf_generic)foo, name);
 }
 
-static int
-use_magicboost(struct unit *user, const struct item_type *itype, int amount,
-struct order *ord)
-{
-    int mtoes =
-        get_pooled(user, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK,
-        user->number);
-    faction *f = user->faction;
-    if (user->number > mtoes) {
-        ADDMSG(&user->faction->msgs, msg_message("use_singleperson",
-            "unit item region command", user, itype->rtype, user->region, ord));
-        return -1;
-    }
-    if (!is_mage(user) || key_get(f->attribs, atoi36("mbst"))) {
-        cmistake(user, user->thisorder, 214, MSG_EVENT);
-        return -1;
-    }
-    use_pooled(user, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK,
-        user->number);
-
-    key_set(&f->attribs, atoi36("mbst"), turn);
-    set_level(user, SK_MAGIC, 3);
-
-    ADDMSG(&user->faction->msgs, msg_message("use_item",
-        "unit item", user, itype->rtype));
-
-    return 0;
-}
-
 static void init_oldpotions(void)
 {
     const char *potionnames[MAX_POTIONS] = {
@@ -1085,8 +1056,5 @@ void register_resources(void)
     register_function((pf_generic)res_changehp, "changehp");
     register_function((pf_generic)res_changeaura, "changeaura");
 
-    register_item_use(use_potion_delayed, "use_p2");
-    register_item_use(use_magicboost, "usemagicboost");
-
     register_item_give(give_horses, "givehorses");
 }

From 75ce6fd23a4d965d3f531a1764bb866cf3598c43 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Tue, 28 Feb 2017 03:48:41 +0100
Subject: [PATCH 27/42] regaura=1 is default

---
 clibs                  | 2 +-
 res/races/goblin-2.xml | 2 +-
 res/races/goblin-3.xml | 2 +-
 res/races/goblin.xml   | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/clibs b/clibs
index 27c8b3202..b91413316 160000
--- a/clibs
+++ b/clibs
@@ -1 +1 @@
-Subproject commit 27c8b3202b52766465743c3324fc0b52c5ba4b11
+Subproject commit b91413316ce13044c555084a9f605983586107b4
diff --git a/res/races/goblin-2.xml b/res/races/goblin-2.xml
index f26fa780e..b092dfaaa 100644
--- a/res/races/goblin-2.xml
+++ b/res/races/goblin-2.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" ?>
-<race name="goblin" magres="-5" maxaura="1.0" regaura="1.0"
+<race name="goblin" magres="-5" maxaura="1.0"
 recruitcost="60" maintenance="6" weight="600" capacity="440"
 speed="1.0" hp="16" damage="1d5" unarmedattack="-2" unarmeddefense="0"
 playerrace="yes" walk="yes" giveperson="yes" giveunit="yes"
diff --git a/res/races/goblin-3.xml b/res/races/goblin-3.xml
index 3fd594939..a65d4c99a 100644
--- a/res/races/goblin-3.xml
+++ b/res/races/goblin-3.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" ?>
-<race name="goblin" magres="-5" maxaura="1.0" regaura="1.0"
+<race name="goblin" magres="-5" maxaura="1.0"
 recruitcost="60" maintenance="6" weight="600" capacity="440"
 speed="1.0" hp="16" damage="1d5" unarmedattack="-2" unarmeddefense="0"
 playerrace="yes" walk="yes" giveperson="yes" giveunit="yes"
diff --git a/res/races/goblin.xml b/res/races/goblin.xml
index a05d127e3..84d2c5079 100644
--- a/res/races/goblin.xml
+++ b/res/races/goblin.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" ?>
 <race name="goblin" magres="-5" maxaura="1.000000"
-regaura="1.000000" recruitcost="40" maintenance="10" weight="600"
+recruitcost="40" maintenance="10" weight="600"
 capacity="440" speed="1.000000" hp="16" damage="1d5"
 unarmedattack="-2" unarmeddefense="0" playerrace="yes" walk="yes"
 giveperson="yes" giveunit="yes" getitem="yes" equipment="yes"

From 9871d6f23f79711d7777022ba1f2c83aa5cea6ef Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Tue, 28 Feb 2017 04:03:36 +0100
Subject: [PATCH 28/42] remove unused files

---
 res/races.xml          | 14 --------------
 res/races/goblin-2.xml | 24 ------------------------
 2 files changed, 38 deletions(-)
 delete mode 100644 res/races.xml
 delete mode 100644 res/races/goblin-2.xml

diff --git a/res/races.xml b/res/races.xml
deleted file mode 100644
index fcfc1742b..000000000
--- a/res/races.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" ?>
-<races xmlns:xi="http://www.w3.org/2001/XInclude">
-  <xi:include href="races/aquarian.xml"/>
-  <xi:include href="races/cat.xml"/>
-  <xi:include href="races/demon.xml"/>
-  <xi:include href="races/dwarf.xml"/>
-  <xi:include href="races/elf.xml"/>
-  <xi:include href="races/goblin.xml"/>
-  <xi:include href="races/halfling.xml"/>
-  <xi:include href="races/human.xml"/>
-  <xi:include href="races/insect.xml"/>
-  <xi:include href="races/orc.xml"/>
-  <xi:include href="races/troll.xml"/>
-</races>
diff --git a/res/races/goblin-2.xml b/res/races/goblin-2.xml
deleted file mode 100644
index b092dfaaa..000000000
--- a/res/races/goblin-2.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" ?>
-<race name="goblin" magres="-5" maxaura="1.0"
-recruitcost="60" maintenance="6" weight="600" capacity="440"
-speed="1.0" hp="16" damage="1d5" unarmedattack="-2" unarmeddefense="0"
-playerrace="yes" walk="yes" giveperson="yes" giveunit="yes"
-getitem="yes" equipment="yes" healing="2.0">
-  <ai splitsize="10000" moverandom="yes" learn="yes"/>
-  <param name="hunger.damage" value="1d8+7"/>
-  <param name="other_race" value="demon"/>
-  <param name="recruit_multi" value="0.5"/>
-  <skill name="building" modifier="1"/>
-  <skill name="cartmaking" modifier="-1"/>
-  <skill name="catapult" modifier="1"/>
-  <skill name="magic" modifier="-99"/>
-  <skill name="mining" modifier="1"/>
-  <skill name="roadwork" modifier="-2"/>
-  <skill name="sailing" modifier="-2"/>
-  <skill name="shipcraft" modifier="-2"/>
-  <skill name="tactics" modifier="-2"/>
-  <skill name="unarmed" modifier="-99"/>
-  <attack type="1" damage="1d5"/>
-  <familiar race="rat" default="yes"/>
-  <familiar race="imp"/>
-</race>

From 5a01eae522bf025b4a681aeb13033f2d4a0b8cbe Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno@eressea.de>
Date: Tue, 28 Feb 2017 10:48:27 +0100
Subject: [PATCH 29/42] make item_use function crash if new and old rules do
 not agree add a 'test' function to E3 that exercises all those items

---
 scripts/tests/e3/init.lua  |  1 +
 scripts/tests/e3/items.lua | 35 ++++++++++++++++++++++++
 src/battle.c               |  6 +++--
 src/kernel/item.h          |  2 ++
 src/kernel/race.c          | 13 +++++++++
 src/kernel/race.h          |  4 +++
 src/kernel/race.test.c     | 54 +++++++++++++++++++++++++++++++++++++-
 7 files changed, 112 insertions(+), 3 deletions(-)
 create mode 100644 scripts/tests/e3/items.lua

diff --git a/scripts/tests/e3/init.lua b/scripts/tests/e3/init.lua
index 95df86c4c..28a2cf6e5 100644
--- a/scripts/tests/e3/init.lua
+++ b/scripts/tests/e3/init.lua
@@ -4,6 +4,7 @@ require 'tests.e3.spells'
 require 'tests.e3.rules'
 require 'tests.e3.parser'
 require 'tests.e3.morale'
+require 'tests.e3.items'
 require 'tests.orders'
 require 'tests.common'
 require 'tests.items'
diff --git a/scripts/tests/e3/items.lua b/scripts/tests/e3/items.lua
new file mode 100644
index 000000000..9c6035bf7
--- /dev/null
+++ b/scripts/tests/e3/items.lua
@@ -0,0 +1,35 @@
+require "lunit"
+
+module("tests.e3.items", package.seeall, lunit.testcase )
+
+function setup()
+    eressea.game.reset()
+    eressea.settings.set("rules.food.flags", "4") -- food is free
+    eressea.settings.set("NewbieImmunity", "0")
+end
+
+function test_goblins()
+    local r = region.create(0, 0, "plain")
+    assert(r)
+    local f1 = faction.create("goblin@eressea.de", "goblin", "de")
+    local f2 = faction.create("dwarf@eressea.de", "dwarf", "de")
+    local f3 = faction.create("elf@eressea.de", "elf", "de")
+    local u1 = unit.create(f1, r, 1)
+    local u2 = unit.create(f2, r, 1)
+    local u3 = unit.create(f3, r, 1)
+
+    local restricted = {
+    "towershield", "rep_crossbow", "plate", "lance",
+    "mllornlance", "greatbow", "greataxe", "axe", "scale",
+    "plate", "rustyhalberd", "halberd", "greatsword"
+    }
+    for k, v in ipairs(restricted) do
+        u1:add_item(v, 1)
+        u2:add_item(v, 1)
+        u2:add_item(v, 1)
+    end
+
+    u1:add_order("ATTACKIERE " .. itoa36(u2.id))
+    u1:add_order("ATTACKIERE " .. itoa36(u3.id))
+    process_orders()
+end
diff --git a/src/battle.c b/src/battle.c
index 9864adc41..9e2958242 100644
--- a/src/battle.c
+++ b/src/battle.c
@@ -576,10 +576,12 @@ static weapon *select_weapon(const troop t, bool attacking,
 
 static bool i_canuse(const unit * u, const item_type * itype)
 {
+    bool result = true;
     if (itype->canuse) {
-        return itype->canuse(u, itype);
+        result = itype->canuse(u, itype);
     }
-    return true;
+    assert(result==rc_can_use(u_race(u), itype));
+    return result;
 }
 
 static int
diff --git a/src/kernel/item.h b/src/kernel/item.h
index 14196ab37..d38a21340 100644
--- a/src/kernel/item.h
+++ b/src/kernel/item.h
@@ -121,6 +121,8 @@ extern "C" {
         unsigned int flags;
         int weight;
         int capacity;
+        int mask_allow;
+        int mask_deny;
         struct construction *construction;
         char *_appearance[2];       /* wie es f�r andere aussieht */
         /* --- functions --- */
diff --git a/src/kernel/race.c b/src/kernel/race.c
index d159fd6d8..efe38dcfe 100644
--- a/src/kernel/race.c
+++ b/src/kernel/race.c
@@ -54,6 +54,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 /* libc includes */
 #include <assert.h>
 #include <ctype.h>
+#include <limits.h>
 #include <math.h>
 #include <stdio.h>
 #include <string.h>
@@ -94,6 +95,7 @@ enum {
     RCO_TRADEHERB
 };
 
+
 static void rc_setoption(race *rc, int k, const char *value) {
     unsigned char key = (unsigned char)k;
     int i;
@@ -323,6 +325,17 @@ bool rc_changed(int *cache) {
     return false;
 }
 
+bool rc_can_use(const struct race *rc, const struct item_type *itype)
+{
+    if (itype->mask_allow) {
+        return (rc->mask_item==0 || (itype->mask_allow & rc->mask_item) != 0);
+    }
+    if (itype->mask_deny) {
+        return (itype->mask_deny & rc->mask_item) == 0;
+    }
+    return true;
+}
+
 race *rc_create(const char *zName)
 {
     race *rc;
diff --git a/src/kernel/race.h b/src/kernel/race.h
index f14b8e54f..fcd78ae19 100644
--- a/src/kernel/race.h
+++ b/src/kernel/race.h
@@ -48,6 +48,7 @@ extern "C" {
     struct spellref;
     struct locale;
     struct rcoption;
+    struct item_type;
 
     extern int num_races;
 
@@ -140,6 +141,7 @@ extern "C" {
         int flags;
         int battle_flags;
         int ec_flags;
+        int mask_item;
         struct att attack[RACE_ATTACKS];
         signed char bonus[MAXSKILLS];
 
@@ -174,6 +176,8 @@ extern "C" {
     const race *rc_find(const char *);
     void free_races(void);
 
+    bool rc_can_use(const struct race *rc, const struct item_type *itype);
+
     typedef enum name_t { NAME_SINGULAR, NAME_PLURAL, NAME_DEFINITIVE, NAME_CATEGORY } name_t;
     const char * rc_name_s(const race *rc, name_t n);
     const char * rc_name(const race *rc, name_t n, char *name, size_t size);
diff --git a/src/kernel/race.test.c b/src/kernel/race.test.c
index 07ecbdd45..b292a825e 100644
--- a/src/kernel/race.test.c
+++ b/src/kernel/race.test.c
@@ -1,10 +1,13 @@
 #include <platform.h>
 #include <kernel/config.h>
 #include "race.h"
-#include <CuTest.h>
+#include "item.h"
+
 #include <tests.h>
+#include <CuTest.h>
 
 #include <stdlib.h>
+#include <limits.h>
 #include <assert.h>
 
 static void test_rc_name(CuTest *tc) {
@@ -100,6 +103,54 @@ static void test_rc_set_param(CuTest *tc) {
     test_cleanup();
 }
 
+static void test_rc_can_use(CuTest *tc) {
+    race *rc;
+    item_type *itype;
+
+    test_setup();
+    rc = test_create_race("goblin");
+    itype = test_create_itemtype("plate");
+    CuAssertTrue(tc, rc_can_use(rc, itype));
+
+    /* default case. all items and races in E2 */
+    itype->mask_deny = 0;
+    rc->mask_item = 0;
+    CuAssertTrue(tc, rc_can_use(rc, itype));
+
+    /* some race is forbidden from using this item. */
+    itype->mask_deny = 1;
+
+    /* we are not that race. */
+    rc->mask_item = 2;
+    CuAssertTrue(tc, rc_can_use(rc, itype));
+
+    /* we are that race */
+    rc->mask_item = 1;
+    CuAssertTrue(tc, ! rc_can_use(rc, itype));
+
+    /* we are not a special race at all */
+    rc->mask_item = 0;
+    CuAssertTrue(tc, rc_can_use(rc, itype));
+
+    /* only one race is allowed to use this item */
+    itype->mask_deny = 0;
+    itype->mask_allow = 1;
+
+    /* we are not that race */
+    rc->mask_item = 2;
+    CuAssertTrue(tc, ! rc_can_use(rc, itype));
+    
+    /* we are that race */
+    rc->mask_item = 1;
+    CuAssertTrue(tc, rc_can_use(rc, itype));
+    
+    /* we are not special */
+    rc->mask_item = 0;
+    CuAssertTrue(tc, rc_can_use(rc, itype));
+    
+    test_cleanup();
+}
+
 CuSuite *get_race_suite(void)
 {
     CuSuite *suite = CuSuiteNew();
@@ -109,6 +160,7 @@ CuSuite *get_race_suite(void)
     SUITE_ADD_TEST(suite, test_rc_defaults);
     SUITE_ADD_TEST(suite, test_rc_find);
     SUITE_ADD_TEST(suite, test_rc_set_param);
+    SUITE_ADD_TEST(suite, test_rc_can_use);
     return suite;
 }
 

From c276b5a43ca2e1471d9aa33d3451a30f42930342 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno@eressea.de>
Date: Tue, 28 Feb 2017 10:58:09 +0100
Subject: [PATCH 30/42] generate error, not crash. todo: fix items

---
 scripts/tests/e3/items.lua | 2 +-
 src/battle.c               | 7 ++++++-
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/scripts/tests/e3/items.lua b/scripts/tests/e3/items.lua
index 9c6035bf7..b2100f5ed 100644
--- a/scripts/tests/e3/items.lua
+++ b/scripts/tests/e3/items.lua
@@ -26,7 +26,7 @@ function test_goblins()
     for k, v in ipairs(restricted) do
         u1:add_item(v, 1)
         u2:add_item(v, 1)
-        u2:add_item(v, 1)
+        u3:add_item(v, 1)
     end
 
     u1:add_order("ATTACKIERE " .. itoa36(u2.id))
diff --git a/src/battle.c b/src/battle.c
index 9e2958242..fa5725c50 100644
--- a/src/battle.c
+++ b/src/battle.c
@@ -580,7 +580,12 @@ static bool i_canuse(const unit * u, const item_type * itype)
     if (itype->canuse) {
         result = itype->canuse(u, itype);
     }
-    assert(result==rc_can_use(u_race(u), itype));
+    if (result!=rc_can_use(u_race(u), itype)) {
+        log_error("conversion error: %s should be %s to use %s",
+            u->_race->_name,
+            result ? "allowed" : "forbidden",
+            itype->rtype->_name);
+    }
     return result;
 }
 

From 2eb88c472cbf9cb40def53514c24b0cba5412a05 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno@eressea.de>
Date: Tue, 28 Feb 2017 11:07:32 +0100
Subject: [PATCH 31/42] have I bollocksed up the deny/allow rules? looks like
 it

---
 res/e3a/armor/towershield.xml |  2 +-
 res/e3a/races.xml             | 18 +++++++++++++++---
 res/races/goblin-3.xml        |  2 +-
 scripts/tests/e3/items.lua    | 13 +++++++++----
 src/kernel/xmlreader.c        |  3 +++
 5 files changed, 29 insertions(+), 9 deletions(-)

diff --git a/res/e3a/armor/towershield.xml b/res/e3a/armor/towershield.xml
index e80c03523..dcd2858cf 100644
--- a/res/e3a/armor/towershield.xml
+++ b/res/e3a/armor/towershield.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0"?>
 <resource name="towershield">
-  <item weight="200" score="60">
+  <item weight="200" score="60" allow="1">
     <function name="canuse" value="lua_canuse_item"/>
     <construction skill="armorer" minskill="4">
       <modifier function="mod_dwarves_only"/>
diff --git a/res/e3a/races.xml b/res/e3a/races.xml
index 2e5a81aee..fc16645c6 100644
--- a/res/e3a/races.xml
+++ b/res/e3a/races.xml
@@ -71,7 +71,11 @@
     <familiar race="ghost"/>
   </race>
 
-  <race name="halfling" defensemodifier="1" magres="5" maxaura="1.0" regaura="1.0" recruitcost="100" maintenance="10" weight="1000" capacity="540" speed="1.0" hp="20" damage="1d5" unarmedattack="-2" unarmeddefense="-2" playerrace="yes" walk="yes" giveperson="yes" giveunit="yes" getitem="yes" equipment="yes">
+  <race name="halfling" defensemodifier="1" magres="5" maxaura="1.0"
+  regaura="1.0" recruitcost="100" maintenance="10" weight="1000"
+  capacity="540" speed="1.0" hp="20" damage="1d5" unarmedattack="-2"
+  unarmeddefense="-2" playerrace="yes" walk="yes" giveperson="yes"
+  giveunit="yes" getitem="yes" equipment="yes" items="8">
     <ai splitsize="10000" moverandom="yes" learn="yes"/>
     <param name="other_race" value="dwarf"/>
     <param name="luxury_trade" value="600"/>
@@ -116,7 +120,11 @@
     <familiar race="ghost"/>
   </race>
 
-  <race name="elf" magres="10" maxaura="1.0" regaura="1.1" recruitcost="200" maintenance="10" weight="1000" capacity="540" speed="1.0" hp="20" damage="1d5" unarmedattack="-2" unarmeddefense="-2" playerrace="yes" walk="yes" giveperson="yes" giveunit="yes" getitem="yes" equipment="yes">
+  <race name="elf" magres="10" maxaura="1.0" regaura="1.1"
+  recruitcost="200" maintenance="10" weight="1000" capacity="540"
+  speed="1.0" hp="20" damage="1d5" unarmedattack="-2"
+  unarmeddefense="-2" playerrace="yes" walk="yes" giveperson="yes"
+  giveunit="yes" getitem="yes" equipment="yes" items="2">
     <ai splitsize="10000" moverandom="yes" learn="yes"/>
     <skill name="armorer" modifier="-1"/>
     <skill name="bow" modifier="2"/>
@@ -156,7 +164,11 @@
     <familiar race="rat"/>
   </race>
 
-  <race name="dwarf" magres="5" maxaura="1.0" regaura="0.9" recruitcost="240" maintenance="10" weight="1000" capacity="540" speed="1.0" hp="20" damage="1d5" unarmedattack="-2" unarmeddefense="-2" playerrace="yes" walk="yes" giveperson="yes" giveunit="yes" getitem="yes" equipment="yes">
+  <race name="dwarf" magres="5" maxaura="1.0" regaura="0.9"
+  recruitcost="240" maintenance="10" weight="1000" capacity="540"
+  speed="1.0" hp="20" damage="1d5" unarmedattack="-2"
+  unarmeddefense="-2" playerrace="yes" walk="yes" giveperson="yes"
+  giveunit="yes" getitem="yes" equipment="yes" items="1">
     <ai splitsize="10000" moverandom="yes" learn="yes"/>
     <skill name="armorer" modifier="2"/>
     <skill name="bow" modifier="-1"/>
diff --git a/res/races/goblin-3.xml b/res/races/goblin-3.xml
index a65d4c99a..8deb33f5e 100644
--- a/res/races/goblin-3.xml
+++ b/res/races/goblin-3.xml
@@ -3,7 +3,7 @@
 recruitcost="60" maintenance="6" weight="600" capacity="440"
 speed="1.0" hp="16" damage="1d5" unarmedattack="-2" unarmeddefense="0"
 playerrace="yes" walk="yes" giveperson="yes" giveunit="yes"
-getitem="yes" equipment="yes" healing="2.0">
+getitem="yes" equipment="yes" healing="2.0" items="4">
   <ai splitsize="10000" moverandom="yes" learn="yes"/>
   <param name="hunger.damage" value="1d8+7"/>
   <param name="other_race" value="demon"/>
diff --git a/scripts/tests/e3/items.lua b/scripts/tests/e3/items.lua
index b2100f5ed..5594eed67 100644
--- a/scripts/tests/e3/items.lua
+++ b/scripts/tests/e3/items.lua
@@ -14,7 +14,9 @@ function test_goblins()
     local f1 = faction.create("goblin@eressea.de", "goblin", "de")
     local f2 = faction.create("dwarf@eressea.de", "dwarf", "de")
     local f3 = faction.create("elf@eressea.de", "elf", "de")
-    local u1 = unit.create(f1, r, 1)
+    local ud = unit.create(f1, r, 1)
+    local uh = unit.create(f1, r, 1)
+    uh.race = "halfling"
     local u2 = unit.create(f2, r, 1)
     local u3 = unit.create(f3, r, 1)
 
@@ -24,12 +26,15 @@ function test_goblins()
     "plate", "rustyhalberd", "halberd", "greatsword"
     }
     for k, v in ipairs(restricted) do
-        u1:add_item(v, 1)
+        ud:add_item(v, 1)
+        uh:add_item(v, 1)
         u2:add_item(v, 1)
         u3:add_item(v, 1)
     end
 
-    u1:add_order("ATTACKIERE " .. itoa36(u2.id))
-    u1:add_order("ATTACKIERE " .. itoa36(u3.id))
+    uh:add_order("ATTACKIERE " .. itoa36(u2.id))
+    uh:add_order("ATTACKIERE " .. itoa36(u3.id))
+    ud:add_order("ATTACKIERE " .. itoa36(u2.id))
+    ud:add_order("ATTACKIERE " .. itoa36(u3.id))
     process_orders()
 end
diff --git a/src/kernel/xmlreader.c b/src/kernel/xmlreader.c
index af0a9dd23..3a6ba6d81 100644
--- a/src/kernel/xmlreader.c
+++ b/src/kernel/xmlreader.c
@@ -782,6 +782,8 @@ static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype)
     itype = rtype->itype ? rtype->itype : it_get_or_create(rtype);
     itype->weight = xml_ivalue(node, "weight", 0);
     itype->capacity = xml_ivalue(node, "capacity", 0);
+    itype->mask_allow = xml_ivalue(node, "allow", 0);
+    itype->mask_deny = xml_ivalue(node, "deny", 0);
     itype->flags |= flags;
 
     /* reading item/construction */
@@ -1575,6 +1577,7 @@ static int parse_races(xmlDocPtr doc)
         rc->speed = (float)xml_fvalue(node, "speed", rc->speed);
         rc->hitpoints = xml_ivalue(node, "hp", rc->hitpoints);
         rc->armor = (char)xml_ivalue(node, "ac", rc->armor);
+        rc->mask_item = (char)xml_ivalue(node, "items", rc->mask_item);
         study_speed_base = xml_ivalue(node, "studyspeed", 0);
 
         rc->at_default = (char)xml_ivalue(node, "unarmedattack", -2);

From 6e8e16309989bbfc7251ca77e7c805de5e8c5d11 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno@eressea.de>
Date: Fri, 3 Mar 2017 09:56:27 +0100
Subject: [PATCH 32/42] make item allow/deny XML easier to read, fix code

---
 res/core/weapons/greatbow.xml     |  2 +-
 res/core/weapons/rep_crossbow.xml |  2 +-
 res/e3a/armor/plate.xml           |  2 +-
 res/e3a/armor/scale.xml           |  2 +-
 res/e3a/armor/towershield.xml     |  2 +-
 res/e3a/races.xml                 |  6 +++---
 res/e3a/weapons.xml               |  4 ++--
 res/e3a/weapons/greatbow.xml      |  2 +-
 res/e3a/weapons/halberd.xml       |  2 +-
 res/e3a/weapons/mallornlance.xml  |  2 +-
 res/e3a/weapons/rustyhalberd.xml  |  2 +-
 res/races/goblin-3.xml            |  2 +-
 scripts/tests/e3/items.lua        |  4 ++--
 src/kernel/race.c                 |  2 +-
 src/kernel/race.test.c            |  2 +-
 src/kernel/xmlreader.c            | 26 +++++++++++++++++++++++---
 16 files changed, 42 insertions(+), 22 deletions(-)

diff --git a/res/core/weapons/greatbow.xml b/res/core/weapons/greatbow.xml
index 10b270e55..14734a129 100644
--- a/res/core/weapons/greatbow.xml
+++ b/res/core/weapons/greatbow.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0"?>
 <resource name="greatbow">
-  <item weight="100">
+  <item weight="100" allow="elf">
     <function name="canuse" value="lua_canuse_item"/>
     <construction skill="weaponsmithing" minskill="5">
       <modifier function="mod_elves_only"/>
diff --git a/res/core/weapons/rep_crossbow.xml b/res/core/weapons/rep_crossbow.xml
index 66e3f9fc2..d6f869046 100644
--- a/res/core/weapons/rep_crossbow.xml
+++ b/res/core/weapons/rep_crossbow.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0"?>
 <resource name="rep_crossbow">
-  <item weight="100">
+  <item weight="100" allow="dwarf halfling">
     <function name="canuse" value="lua_canuse_item"/>
     <construction skill="weaponsmithing" minskill="5">
       <modifier function="mod_dwarves_only"/>
diff --git a/res/e3a/armor/plate.xml b/res/e3a/armor/plate.xml
index 29cabbf1a..9aeafc3c1 100644
--- a/res/e3a/armor/plate.xml
+++ b/res/e3a/armor/plate.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0"?>
 <resource name="plate">
-  <item weight="400" score="150">
+  <item weight="400" score="150" deny="goblin">
     <function name="canuse" value="lua_canuse_item"/>
     <construction skill="armorer" minskill="4">
       <requirement type="iron" quantity="4"/>
diff --git a/res/e3a/armor/scale.xml b/res/e3a/armor/scale.xml
index 7039b4008..691ce5ba7 100644
--- a/res/e3a/armor/scale.xml
+++ b/res/e3a/armor/scale.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0"?>
 <resource name="scale">
-  <item weight="300" score="150">
+  <item weight="300" score="150" allow="dwarf halfling">
     <function name="canuse" value="lua_canuse_item"/>
     <construction skill="armorer" minskill="5">
       <modifier function="mod_dwarves_only"/>
diff --git a/res/e3a/armor/towershield.xml b/res/e3a/armor/towershield.xml
index dcd2858cf..78e113e2a 100644
--- a/res/e3a/armor/towershield.xml
+++ b/res/e3a/armor/towershield.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0"?>
 <resource name="towershield">
-  <item weight="200" score="60" allow="1">
+  <item weight="200" score="60" allow="dwarf">
     <function name="canuse" value="lua_canuse_item"/>
     <construction skill="armorer" minskill="4">
       <modifier function="mod_dwarves_only"/>
diff --git a/res/e3a/races.xml b/res/e3a/races.xml
index fc16645c6..8b0cfae6d 100644
--- a/res/e3a/races.xml
+++ b/res/e3a/races.xml
@@ -75,7 +75,7 @@
   regaura="1.0" recruitcost="100" maintenance="10" weight="1000"
   capacity="540" speed="1.0" hp="20" damage="1d5" unarmedattack="-2"
   unarmeddefense="-2" playerrace="yes" walk="yes" giveperson="yes"
-  giveunit="yes" getitem="yes" equipment="yes" items="8">
+  giveunit="yes" getitem="yes" equipment="yes">
     <ai splitsize="10000" moverandom="yes" learn="yes"/>
     <param name="other_race" value="dwarf"/>
     <param name="luxury_trade" value="600"/>
@@ -124,7 +124,7 @@
   recruitcost="200" maintenance="10" weight="1000" capacity="540"
   speed="1.0" hp="20" damage="1d5" unarmedattack="-2"
   unarmeddefense="-2" playerrace="yes" walk="yes" giveperson="yes"
-  giveunit="yes" getitem="yes" equipment="yes" items="2">
+  giveunit="yes" getitem="yes" equipment="yes" >
     <ai splitsize="10000" moverandom="yes" learn="yes"/>
     <skill name="armorer" modifier="-1"/>
     <skill name="bow" modifier="2"/>
@@ -168,7 +168,7 @@
   recruitcost="240" maintenance="10" weight="1000" capacity="540"
   speed="1.0" hp="20" damage="1d5" unarmedattack="-2"
   unarmeddefense="-2" playerrace="yes" walk="yes" giveperson="yes"
-  giveunit="yes" getitem="yes" equipment="yes" items="1">
+  giveunit="yes" getitem="yes" equipment="yes">
     <ai splitsize="10000" moverandom="yes" learn="yes"/>
     <skill name="armorer" modifier="2"/>
     <skill name="bow" modifier="-1"/>
diff --git a/res/e3a/weapons.xml b/res/e3a/weapons.xml
index 052266a02..96a987b28 100644
--- a/res/e3a/weapons.xml
+++ b/res/e3a/weapons.xml
@@ -1,9 +1,7 @@
 <?xml version="1.0"?>
 <resources xmlns:xi="http://www.w3.org/2001/XInclude">
-  <xi:include href="config://core/weapons/axe.xml"/>
   <xi:include href="config://core/weapons/bow.xml"/>
   <xi:include href="config://core/weapons/catapult.xml"/>
-  <xi:include href="config://core/weapons/lance.xml"/>
   <xi:include href="config://core/weapons/mallornbow.xml"/>
   <xi:include href="config://core/weapons/mallornspear.xml"/>
   <xi:include href="config://core/weapons/rep_crossbow.xml"/>
@@ -13,6 +11,8 @@
   <xi:include href="config://core/weapons/spear.xml"/>
   <xi:include href="config://core/weapons/sword.xml"/>
   <xi:include href="config://core/weapons/firesword.xml"/>
+  <xi:include href="config://game/weapons/lance.xml"/>
+  <xi:include href="config://game/weapons/axe.xml"/>
   <xi:include href="config://game/weapons/crossbow.xml"/>
   <xi:include href="config://game/weapons/greatbow.xml"/>
   <xi:include href="config://game/weapons/greatsword.xml"/>
diff --git a/res/e3a/weapons/greatbow.xml b/res/e3a/weapons/greatbow.xml
index 7f08427c8..b488a011c 100644
--- a/res/e3a/weapons/greatbow.xml
+++ b/res/e3a/weapons/greatbow.xml
@@ -4,7 +4,7 @@
      * has lower damage
 -->
 <resource name="greatbow">
-  <item weight="100">
+  <item weight="100" allow="elf">
     <function name="canuse" value="lua_canuse_item"/>
     <construction skill="weaponsmithing" minskill="5">
       <modifier function="mod_elves_only"/>
diff --git a/res/e3a/weapons/halberd.xml b/res/e3a/weapons/halberd.xml
index 2c11b62fc..c07cbfd4d 100644
--- a/res/e3a/weapons/halberd.xml
+++ b/res/e3a/weapons/halberd.xml
@@ -3,7 +3,7 @@
   1. you cannt use this with cavalry
 -->
 <resource name="halberd">
-  <item weight="200">
+  <item weight="200" deny="goblin">
     <function name="canuse" value="lua_canuse_item"/>
     <construction skill="weaponsmithing" minskill="3">
       <requirement type="log" quantity="2"/>
diff --git a/res/e3a/weapons/mallornlance.xml b/res/e3a/weapons/mallornlance.xml
index 1fcad08c8..d2aacaea7 100644
--- a/res/e3a/weapons/mallornlance.xml
+++ b/res/e3a/weapons/mallornlance.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0"?>
 <resource name="mallornlance">
-  <item weight="100">
+  <item weight="100" deny="goblin">
     <function name="canuse" value="lua_canuse_item"/>
     <construction skill="weaponsmithing" minskill="5">
       <requirement type="mallorn" quantity="2"/>
diff --git a/res/e3a/weapons/rustyhalberd.xml b/res/e3a/weapons/rustyhalberd.xml
index ed25d5db7..32c0d4497 100644
--- a/res/e3a/weapons/rustyhalberd.xml
+++ b/res/e3a/weapons/rustyhalberd.xml
@@ -3,7 +3,7 @@
   1. you cannot use this with cavalry
 -->
 <resource name="rustyhalberd">
-  <item weight="200" score="20">
+  <item weight="200" score="20" deny="goblin">
     <function name="canuse" value="lua_canuse_item"/>
     <construction skill="weaponsmithing" minskill="3">
       <requirement type="iron" quantity="1"/>
diff --git a/res/races/goblin-3.xml b/res/races/goblin-3.xml
index 8deb33f5e..a65d4c99a 100644
--- a/res/races/goblin-3.xml
+++ b/res/races/goblin-3.xml
@@ -3,7 +3,7 @@
 recruitcost="60" maintenance="6" weight="600" capacity="440"
 speed="1.0" hp="16" damage="1d5" unarmedattack="-2" unarmeddefense="0"
 playerrace="yes" walk="yes" giveperson="yes" giveunit="yes"
-getitem="yes" equipment="yes" healing="2.0" items="4">
+getitem="yes" equipment="yes" healing="2.0">
   <ai splitsize="10000" moverandom="yes" learn="yes"/>
   <param name="hunger.damage" value="1d8+7"/>
   <param name="other_race" value="demon"/>
diff --git a/scripts/tests/e3/items.lua b/scripts/tests/e3/items.lua
index 5594eed67..349e12b2a 100644
--- a/scripts/tests/e3/items.lua
+++ b/scripts/tests/e3/items.lua
@@ -22,8 +22,8 @@ function test_goblins()
 
     local restricted = {
     "towershield", "rep_crossbow", "plate", "lance",
-    "mllornlance", "greatbow", "greataxe", "axe", "scale",
-    "plate", "rustyhalberd", "halberd", "greatsword"
+    "mallornlance", "greatbow", "greataxe", "axe", "scale",
+    "plate", "halberd", "greatsword", "rustyhalberd"
     }
     for k, v in ipairs(restricted) do
         ud:add_item(v, 1)
diff --git a/src/kernel/race.c b/src/kernel/race.c
index efe38dcfe..8a8cc906e 100644
--- a/src/kernel/race.c
+++ b/src/kernel/race.c
@@ -328,7 +328,7 @@ bool rc_changed(int *cache) {
 bool rc_can_use(const struct race *rc, const struct item_type *itype)
 {
     if (itype->mask_allow) {
-        return (rc->mask_item==0 || (itype->mask_allow & rc->mask_item) != 0);
+        return (itype->mask_allow & rc->mask_item) != 0;
     }
     if (itype->mask_deny) {
         return (itype->mask_deny & rc->mask_item) == 0;
diff --git a/src/kernel/race.test.c b/src/kernel/race.test.c
index b292a825e..2db76a6fd 100644
--- a/src/kernel/race.test.c
+++ b/src/kernel/race.test.c
@@ -146,7 +146,7 @@ static void test_rc_can_use(CuTest *tc) {
     
     /* we are not special */
     rc->mask_item = 0;
-    CuAssertTrue(tc, rc_can_use(rc, itype));
+    CuAssertTrue(tc, ! rc_can_use(rc, itype));
     
     test_cleanup();
 }
diff --git a/src/kernel/xmlreader.c b/src/kernel/xmlreader.c
index 3a6ba6d81..208a586a7 100644
--- a/src/kernel/xmlreader.c
+++ b/src/kernel/xmlreader.c
@@ -757,6 +757,27 @@ static weapon_type *xml_readweapon(xmlXPathContextPtr xpath, item_type * itype)
     return wtype;
 }
 
+static int race_mask = 1;
+
+static void mask_races(xmlNodePtr node, const char *key, int *maskp) {
+    xmlChar *propValue = xmlGetProp(node, BAD_CAST key);
+    char *tok;
+    int mask = 0;
+    assert(maskp);
+    tok = strtok((char *)propValue, " ,");
+    while (tok) {
+        race * rc = rc_get_or_create(tok);
+        if (!rc->mask_item) {
+            rc->mask_item = race_mask;
+            race_mask = race_mask << 1;
+        }
+        mask |= rc->mask_item;
+        tok = strtok(NULL, " ,");
+    }
+    *maskp = mask;
+    xmlFree(propValue);
+}
+
 static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype)
 {
     xmlNodePtr node = xpath->node;
@@ -782,8 +803,8 @@ static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype)
     itype = rtype->itype ? rtype->itype : it_get_or_create(rtype);
     itype->weight = xml_ivalue(node, "weight", 0);
     itype->capacity = xml_ivalue(node, "capacity", 0);
-    itype->mask_allow = xml_ivalue(node, "allow", 0);
-    itype->mask_deny = xml_ivalue(node, "deny", 0);
+    mask_races(node, "allow", &itype->mask_allow);
+    mask_races(node, "deny", &itype->mask_deny);
     itype->flags |= flags;
 
     /* reading item/construction */
@@ -1577,7 +1598,6 @@ static int parse_races(xmlDocPtr doc)
         rc->speed = (float)xml_fvalue(node, "speed", rc->speed);
         rc->hitpoints = xml_ivalue(node, "hp", rc->hitpoints);
         rc->armor = (char)xml_ivalue(node, "ac", rc->armor);
-        rc->mask_item = (char)xml_ivalue(node, "items", rc->mask_item);
         study_speed_base = xml_ivalue(node, "studyspeed", 0);
 
         rc->at_default = (char)xml_ivalue(node, "unarmedattack", -2);

From 5b1e786fb262090c63e3d7490dd357e4c5d60018 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno@eressea.de>
Date: Fri, 3 Mar 2017 10:35:34 +0100
Subject: [PATCH 33/42] fix bad strtok call

---
 src/kernel/xmlreader.c | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/src/kernel/xmlreader.c b/src/kernel/xmlreader.c
index 208a586a7..d6397edf5 100644
--- a/src/kernel/xmlreader.c
+++ b/src/kernel/xmlreader.c
@@ -761,21 +761,22 @@ static int race_mask = 1;
 
 static void mask_races(xmlNodePtr node, const char *key, int *maskp) {
     xmlChar *propValue = xmlGetProp(node, BAD_CAST key);
-    char *tok;
     int mask = 0;
     assert(maskp);
-    tok = strtok((char *)propValue, " ,");
-    while (tok) {
-        race * rc = rc_get_or_create(tok);
-        if (!rc->mask_item) {
-            rc->mask_item = race_mask;
-            race_mask = race_mask << 1;
+    if (propValue) {
+        char * tok = strtok((char *)propValue, " ,");
+        while (tok) {
+            race * rc = rc_get_or_create(tok);
+            if (!rc->mask_item) {
+                rc->mask_item = race_mask;
+                race_mask = race_mask << 1;
+            }
+            mask |= rc->mask_item;
+            tok = strtok(NULL, " ,");
         }
-        mask |= rc->mask_item;
-        tok = strtok(NULL, " ,");
+        xmlFree(propValue);
     }
     *maskp = mask;
-    xmlFree(propValue);
 }
 
 static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype)

From 591a5b67d3ee9af6aa9b2325c8ed5764c1201c67 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Fri, 3 Mar 2017 16:38:28 +0100
Subject: [PATCH 34/42] add missing files

---
 res/e3a/weapons/axe.xml   | 14 ++++++++++++++
 res/e3a/weapons/lance.xml | 13 +++++++++++++
 2 files changed, 27 insertions(+)
 create mode 100644 res/e3a/weapons/axe.xml
 create mode 100644 res/e3a/weapons/lance.xml

diff --git a/res/e3a/weapons/axe.xml b/res/e3a/weapons/axe.xml
new file mode 100644
index 000000000..991a2b504
--- /dev/null
+++ b/res/e3a/weapons/axe.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<resource name="axe">
+  <item weight="200" deny="goblin">
+    <function name="canuse" value="lua_canuse_item"/>
+    <construction skill="weaponsmithing" minskill="3">
+      <requirement type="log" quantity="1"/>
+      <requirement type="iron" quantity="1"/>
+    </construction>
+    <weapon cut="true" skill="melee" offmod="1" defmod="-2">
+      <damage type="rider" value="2d6+4"/>
+      <damage type="footman" value="2d6+4"/>
+    </weapon>
+  </item>
+</resource>
diff --git a/res/e3a/weapons/lance.xml b/res/e3a/weapons/lance.xml
new file mode 100644
index 000000000..a3f641833
--- /dev/null
+++ b/res/e3a/weapons/lance.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<resource name="lance">
+  <item weight="200" deny="goblin">
+    <function name="canuse" value="lua_canuse_item"/>
+    <construction skill="weaponsmithing" minskill="2">
+      <requirement type="log" quantity="2"/>
+    </construction>
+    <weapon pierce="true" skill="polearm" offmod="0" defmod="-2">
+      <damage type="footman" value="1d5"/>
+      <damage type="rider" value="2d6+5"/>
+    </weapon>
+  </item>
+</resource>

From 259d7d9e804f6169c85250688a801a82ddae9654 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Fri, 3 Mar 2017 16:50:43 +0100
Subject: [PATCH 35/42] add a test for giving horses to 0. should add those
 horses to the region. remove horses from test that is about 50% silver.

---
 scripts/tests/common.lua   | 13 +++++++++++++
 scripts/tests/e3/items.lua | 13 +++++++++++++
 scripts/tests/e3/rules.lua |  6 +-----
 3 files changed, 27 insertions(+), 5 deletions(-)

diff --git a/scripts/tests/common.lua b/scripts/tests/common.lua
index 03ae5db85..317380417 100644
--- a/scripts/tests/common.lua
+++ b/scripts/tests/common.lua
@@ -1028,3 +1028,16 @@ function test_recruit()
         assert_equal(6, u.number)
     end
 end
+
+function test_give_horses()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+
+    r:set_resource("horse", 0)
+    u:add_item("horse", 20)
+    u:add_order("GIB 0 10 PFERDE")
+    process_orders()
+    assert_equal(10, r:get_resource("horse"))
+    assert_equal(10, u:get_item("horse"))
+end
diff --git a/scripts/tests/e3/items.lua b/scripts/tests/e3/items.lua
index 349e12b2a..d916be744 100644
--- a/scripts/tests/e3/items.lua
+++ b/scripts/tests/e3/items.lua
@@ -8,6 +8,19 @@ function setup()
     eressea.settings.set("NewbieImmunity", "0")
 end
 
+function test_give_horses()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+
+    r:set_resource("horse", 0)
+    u:add_item("charger", 20)
+    u:add_order("GIB 0 10 Streitross")
+    process_orders()
+    assert_equal(10, r:get_resource("horse"))
+    assert_equal(10, u:get_item("charger"))
+end
+
 function test_goblins()
     local r = region.create(0, 0, "plain")
     assert(r)
diff --git a/scripts/tests/e3/rules.lua b/scripts/tests/e3/rules.lua
index 2cba83f21..2ecdf1ff7 100644
--- a/scripts/tests/e3/rules.lua
+++ b/scripts/tests/e3/rules.lua
@@ -626,11 +626,7 @@ function test_give_50_percent_of_money()
     u1:add_order("GIB " .. itoa36(u2.id) .. " 221 Silber")
     u2:clear_orders()
     u2:add_order("HELFEN " .. itoa36(u1.faction.id) .. " GIB")
-    u2:add_item("horse", 100)
-    u2:add_order("GIB 0 ALLES PFERD")
-    local h = r:get_resource("horse")
     process_orders()
-    assert_true(r:get_resource("horse")>=h+100)
     assert_equal(m1-221, u1:get_item("money"))
     assert_equal(m2+110, u2:get_item("money"))
 end
@@ -990,4 +986,4 @@ function test_bug2187()
 --  write_report(f)
       
   set_rule("rules.food.flags", "4")
-end
\ No newline at end of file
+end

From 681a4bdaa8333d5b4e808760a0faf221ed3ae481 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Fri, 3 Mar 2017 17:27:51 +0100
Subject: [PATCH 36/42] dolphins are animals, but not horses.

---
 scripts/tests/common.lua | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/scripts/tests/common.lua b/scripts/tests/common.lua
index 317380417..fa2fd9f35 100644
--- a/scripts/tests/common.lua
+++ b/scripts/tests/common.lua
@@ -1036,8 +1036,11 @@ function test_give_horses()
 
     r:set_resource("horse", 0)
     u:add_item("horse", 20)
-    u:add_order("GIB 0 10 PFERDE")
+    u:add_item("dolphin", 10)
+    u:add_order("GIB 0 10 PFERD")
+    u:add_order("GIB 0 5 DELPHIN")
     process_orders()
     assert_equal(10, r:get_resource("horse"))
+    assert_equal(5, u:get_item("dolphin"))
     assert_equal(10, u:get_item("horse"))
 end

From a265bc9cdb1abc59f9c4145da5317d406602e77b Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Fri, 3 Mar 2017 18:02:43 +0100
Subject: [PATCH 37/42] test giving stuff to 0.

---
 scripts/tests/e3/items.lua | 25 ++++++++++++++++++++++++-
 src/laws.c                 | 13 +++++++++----
 2 files changed, 33 insertions(+), 5 deletions(-)

diff --git a/scripts/tests/e3/items.lua b/scripts/tests/e3/items.lua
index d916be744..caf566468 100644
--- a/scripts/tests/e3/items.lua
+++ b/scripts/tests/e3/items.lua
@@ -23,7 +23,6 @@ end
 
 function test_goblins()
     local r = region.create(0, 0, "plain")
-    assert(r)
     local f1 = faction.create("goblin@eressea.de", "goblin", "de")
     local f2 = faction.create("dwarf@eressea.de", "dwarf", "de")
     local f3 = faction.create("elf@eressea.de", "elf", "de")
@@ -51,3 +50,27 @@ function test_goblins()
     ud:add_order("ATTACKIERE " .. itoa36(u3.id))
     process_orders()
 end
+
+function test_make_horse()
+    eressea.settings.set("rules.horses.growth", "0")
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("horses@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+    u:set_skill("training", 4)
+    r:set_resource("horse", 100)
+    u:add_order("MACHE 1 PFERD")
+    process_orders()
+    assert_equal(1, u:get_item("horse"))
+    assert_equal(99, r:get_resource("horse"))
+
+    u:clear_orders()
+    u:add_order("MACHE 1 STREITROSS")
+    u:add_item("money", 200)
+    u:add_item("iron", 1)
+    process_orders()
+    assert_equal(1, u:get_item("charger"))
+    assert_equal(0, u:get_item("horse"))
+    assert_equal(0, u:get_item("iron"))
+    assert_equal(0, u:get_item("money"))
+    assert_equal(99, r:get_resource("horse"))
+end
diff --git a/src/laws.c b/src/laws.c
index cc10baeb5..b8291440f 100644
--- a/src/laws.c
+++ b/src/laws.c
@@ -333,7 +333,7 @@ int peasant_luck_effect(int peasants, int luck, int maxp, double variance)
 
 #endif
 
-static void peasants(region * r)
+static void peasants(region * r, int rule)
 {
     int peasants = rpeasants(r);
     int money = rmoney(r);
@@ -341,7 +341,7 @@ static void peasants(region * r)
     int n, satiated;
     int dead = 0;
 
-    if (peasants > 0 && config_get_int("rules.peasants.growth", 1)) {
+    if (peasants > 0 && rule > 0) {
         int luck = 0;
         double fraction = peasants * peasant_growth_factor();
         int births = RAND_ROUND(fraction);
@@ -812,6 +812,8 @@ void demographics(void)
     static int last_weeks_season = -1;
     static int current_season = -1;
     int plant_rules = config_get_int("rules.grow.formula", 2);
+    int horse_rules = config_get_int("rules.horses.growth", 1);
+    int peasant_rules = config_get_int("rules.peasants.growth", 1);
     const struct building_type *bt_harbour = bt_find("harbour");
 
     if (current_season < 0) {
@@ -843,7 +845,8 @@ void demographics(void)
                  * und gewandert sind */
 
                 calculate_emigration(r);
-                peasants(r);
+                peasants(r, peasant_rules);
+
                 if (r->age > 20) {
                     double mwp = MAX(region_maxworkers(r), 1);
                     double prob =
@@ -854,7 +857,9 @@ void demographics(void)
                         plagues(r);
                     }
                 }
-                horses(r);
+                if (horse_rules > 0) {
+                    horses(r);
+                }
                 if (plant_rules == 2) { /* E2 */
                     growing_trees(r, current_season, last_weeks_season);
                     growing_herbs(r, current_season, last_weeks_season);

From af28da365da44af452b50cf8790575e0483482bf Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Fri, 3 Mar 2017 18:53:39 +0100
Subject: [PATCH 38/42] test giving silber to peasants.

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

diff --git a/scripts/tests/common.lua b/scripts/tests/common.lua
index fa2fd9f35..c3e2472c4 100644
--- a/scripts/tests/common.lua
+++ b/scripts/tests/common.lua
@@ -1044,3 +1044,18 @@ function test_give_horses()
     assert_equal(5, u:get_item("dolphin"))
     assert_equal(10, u:get_item("horse"))
 end
+
+function test_give_silver()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("noreply@eressea.de", "human", "de")
+    local u = unit.create(f, r, 1)
+
+    r:set_resource("peasant", 0)
+    r:set_resource("money", 11)
+    u:clear_orders()
+    u:add_item("money", 20)
+    u:add_order("GIB 0 10 SILBER")
+    process_orders()
+    assert_equal(21, r:get_resource("money"))
+    assert_equal(10, u:get_item("money"))
+end

From d976ee6f67663c1f0d92bbee458ee8cae009f647 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Fri, 3 Mar 2017 19:17:20 +0100
Subject: [PATCH 39/42] special cases, not callbacks, for money and horses.

---
 res/core/common/items.xml    |  4 +--
 res/core/resources/horse.xml |  3 +-
 res/e3a/items.xml            |  3 +-
 scripts/tests/common.lua     |  8 ++---
 src/give.c                   | 57 +++++++++++++++++++++++++++++++-----
 src/kernel/item.c            | 43 ---------------------------
 src/kernel/item.h            |  2 --
 src/kernel/item.test.c       |  1 -
 src/kernel/xmlreader.c       |  7 +----
 9 files changed, 57 insertions(+), 71 deletions(-)

diff --git a/res/core/common/items.xml b/res/core/common/items.xml
index 8f2bbabf8..35f9af2c5 100644
--- a/res/core/common/items.xml
+++ b/res/core/common/items.xml
@@ -70,9 +70,7 @@
   </resource>
 
   <resource name="elvenhorse">
-    <item weight="5000" notlost="yes" big="yes" score="6000" capacity="7000" animal="yes">
-      <function name="give" value="givehorses"/>
-    </item>
+    <item weight="5000" notlost="yes" big="yes" score="6000" capacity="7000" animal="yes"/>
   </resource>
 
   <resource name="dolphin">
diff --git a/res/core/resources/horse.xml b/res/core/resources/horse.xml
index 243b547db..608fca20a 100644
--- a/res/core/resources/horse.xml
+++ b/res/core/resources/horse.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0"?>
 <resource name="horse" limited="yes">
   <item big="yes" weight="5000" score="10" capacity="7000" animal="yes">
-    <construction skill="training" minskill="1"/>
-    <function name="give" value="givehorses"/>
+    <construction skill="training" minskill="1" />
   </item>
 </resource>
diff --git a/res/e3a/items.xml b/res/e3a/items.xml
index 9e928837e..0399043e5 100644
--- a/res/e3a/items.xml
+++ b/res/e3a/items.xml
@@ -62,13 +62,12 @@
   </resource>
 
   <resource name="charger">
-    <item big="yes" weight="5000" score="10" capacity="7000" animal="yes">
+    <item big="yes" weight="5000" score="20" capacity="7000" animal="yes">
       <construction skill="training" minskill="4">
         <requirement type="money" quantity="200"/>
         <requirement type="iron" quantity="1"/>
         <requirement type="horse" quantity="1"/>
       </construction>
-      <function name="give" value="givehorses"/>
     </item>
   </resource>
 
diff --git a/scripts/tests/common.lua b/scripts/tests/common.lua
index c3e2472c4..4f6ed9cbb 100644
--- a/scripts/tests/common.lua
+++ b/scripts/tests/common.lua
@@ -1035,14 +1035,14 @@ function test_give_horses()
     local u = unit.create(f, r, 1)
 
     r:set_resource("horse", 0)
-    u:add_item("horse", 20)
+    u:add_item("horse", 21)
     u:add_item("dolphin", 10)
-    u:add_order("GIB 0 10 PFERD")
+    u:add_order("GIB 0 7 PFERD")
     u:add_order("GIB 0 5 DELPHIN")
     process_orders()
-    assert_equal(10, r:get_resource("horse"))
+    assert_equal(7, r:get_resource("horse"))
     assert_equal(5, u:get_item("dolphin"))
-    assert_equal(10, u:get_item("horse"))
+    assert_equal(14, u:get_item("horse"))
 end
 
 function test_give_silver()
diff --git a/src/give.c b/src/give.c
index 5abb950a6..884c55283 100644
--- a/src/give.c
+++ b/src/give.c
@@ -19,6 +19,7 @@
 
 /* kernel includes */
 #include <kernel/ally.h>
+#include <kernel/build.h>
 #include <kernel/curse.h>
 #include <kernel/faction.h>
 #include <kernel/item.h>
@@ -147,17 +148,45 @@ int give_quota(const unit * src, const unit * dst, const item_type * type,
     return n;
 }
 
+static int
+give_horses(unit * s, unit * d, const item_type * itype, int n,
+    struct order *ord)
+{
+    if (d == NULL) {
+        region *r = s->region;
+        if (r->land) {
+            rsethorses(r, rhorses(r) + n);
+        }
+        return 0;
+    }
+    return -1;                    /* use the mechanism */
+}
+
+static int
+give_money(unit * s, unit * d, const item_type * itype, int n,
+    struct order *ord)
+{
+    if (d == NULL) {
+        region *r = s->region;
+        if (r->land) {
+            rsetmoney(r, rmoney(r) + n);
+        }
+        return 0;
+    }
+    return -1;                    /* use the mechanism */
+}
+
 int
 give_item(int want, const item_type * itype, unit * src, unit * dest,
 struct order *ord)
 {
     short error = 0;
-    int n, r;
+    int n, delta;
 
     assert(itype != NULL);
     n = get_pooled(src, item2resource(itype), GET_SLACK | GET_POOLED_SLACK, want);
     n = MIN(want, n);
-    r = n;
+    delta = n;
     if (dest && src->faction != dest->faction
         && src->faction->age < GiveRestriction()) {
         if (ord != NULL) {
@@ -178,18 +207,19 @@ struct order *ord)
     else if (itype->flags & ITF_CURSED) {
         error = 25;
     }
-    else if (itype->give == NULL || itype->give(src, dest, itype, n, ord) != 0) {
+    else {
         int use = use_pooled(src, item2resource(itype), GET_SLACK, n);
+
         if (use < n)
             use +=
             use_pooled(src, item2resource(itype), GET_POOLED_SLACK,
             n - use);
         if (dest) {
-            r = give_quota(src, dest, itype, n);
-            i_change(&dest->items, itype, r);
+            delta = give_quota(src, dest, itype, n);
+            i_change(&dest->items, itype, delta);
 #ifdef RESERVE_GIVE
 #ifdef RESERVE_DONATIONS
-            change_reservation(dest, itype, r);
+            change_reservation(dest, itype, delta);
 #else
             if (src->faction == dest->faction) {
                 change_reservation(dest, item2resource(itype), r);
@@ -199,14 +229,25 @@ struct order *ord)
 #if MUSEUM_MODULE && defined(TODO)
             /* TODO: use a trigger for the museum warden! */
             if (a_find(dest->attribs, &at_warden)) {
-                warden_add_give(src, dest, itype, r);
+                warden_add_give(src, dest, itype, delta);
             }
 #endif
             handle_event(dest->attribs, "receive", src);
         }
+        else {
+            /* return horses to the region */
+            if (itype->construction && itype->flags & ITF_ANIMAL) {
+                if (itype->construction->skill == SK_HORSE_TRAINING) {
+                    give_horses(src, dest, itype, n, ord);
+                }
+            }
+            else if (itype->rtype == get_resourcetype(R_SILVER)) {
+                give_money(src, dest, itype, n, ord);
+            }
+        }
         handle_event(src->attribs, "give", dest);
     }
-    add_give(src, dest, n, r, item2resource(itype), ord, error);
+    add_give(src, dest, n, delta, item2resource(itype), ord, error);
     if (error)
         return -1;
     return 0;
diff --git a/src/kernel/item.c b/src/kernel/item.c
index 6bd97e6dd..1197e7ce5 100644
--- a/src/kernel/item.c
+++ b/src/kernel/item.c
@@ -565,46 +565,6 @@ item *i_new(const item_type * itype, int size)
 
 #include "region.h"
 
-static int
-give_horses(unit * s, unit * d, const item_type * itype, int n,
-struct order *ord)
-{
-    if (d == NULL) {
-        int use = use_pooled(s, item2resource(itype), GET_SLACK, n);
-        region *r = s->region;
-        if (use < n) {
-            use +=
-            use_pooled(s, item2resource(itype), GET_RESERVE | GET_POOLED_SLACK,
-            n - use);
-        }
-        if (r->land) {
-            rsethorses(r, rhorses(r) + use);
-        }
-        return 0;
-    }
-    return -1;                    /* use the mechanism */
-}
-
-static int
-give_money(unit * s, unit * d, const item_type * itype, int n,
-struct order *ord)
-{
-    if (d == NULL) {
-        int use = use_pooled(s, item2resource(itype), GET_SLACK, n);
-        region *r = s->region;
-        if (use < n) {
-            use +=
-            use_pooled(s, item2resource(itype), GET_RESERVE | GET_POOLED_SLACK,
-            n - use);
-        }
-        if (r->land) {
-            rsetmoney(r, rmoney(r) + use);
-        }
-        return 0;
-    }
-    return -1;                    /* use the mechanism */
-}
-
 #define R_MINOTHER R_SILVER
 #define R_MINHERB R_PLAIN_1
 #define R_MINPOTION R_FAST
@@ -755,7 +715,6 @@ void init_resources(void)
     rtype->flags |= RTF_ITEM | RTF_POOLED;
     rtype->uchange = res_changeitem;
     rtype->itype = it_get_or_create(rtype);
-    rtype->itype->give = give_money;
 
     rtype = rt_get_or_create(resourcenames[R_PERMAURA]);
     rtype->uchange = res_changepermaura;
@@ -1055,6 +1014,4 @@ void register_resources(void)
     register_function((pf_generic)res_changepermaura, "changepermaura");
     register_function((pf_generic)res_changehp, "changehp");
     register_function((pf_generic)res_changeaura, "changeaura");
-
-    register_item_give(give_horses, "givehorses");
 }
diff --git a/src/kernel/item.h b/src/kernel/item.h
index d38a21340..5f8d879f9 100644
--- a/src/kernel/item.h
+++ b/src/kernel/item.h
@@ -128,8 +128,6 @@ extern "C" {
         /* --- functions --- */
         bool(*canuse) (const struct unit * user,
             const struct item_type * itype);
-        int(*give) (struct unit * src, struct unit * dest,
-            const struct item_type * itm, int number, struct order * ord);
         int score;
     } item_type;
 
diff --git a/src/kernel/item.test.c b/src/kernel/item.test.c
index 52e7d9917..e8b8d79e3 100644
--- a/src/kernel/item.test.c
+++ b/src/kernel/item.test.c
@@ -162,7 +162,6 @@ static void test_core_resources(CuTest *tc) {
     CuAssertPtrNotNull(tc, rtype = rt_find("money"));
     CuAssertPtrNotNull(tc, rtype->itype);
     CuAssertPtrNotNull(tc, rtype->uchange);
-    CuAssertPtrNotNull(tc, rtype->itype->give);
     CuAssertPtrNotNull(tc, rtype = rt_find("peasant"));
     CuAssertPtrEquals(tc, 0, rtype->itype);
     CuAssertPtrNotNull(tc, rtype = rt_find("person"));
diff --git a/src/kernel/xmlreader.c b/src/kernel/xmlreader.c
index d6397edf5..edbaa37f4 100644
--- a/src/kernel/xmlreader.c
+++ b/src/kernel/xmlreader.c
@@ -873,12 +873,7 @@ static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype)
             continue;
         }
         assert(propValue != NULL);
-        if (strcmp((const char *)propValue, "give") == 0) {
-            itype->give =
-                (int(*)(struct unit *, struct unit *, const struct item_type *, int,
-            struct order *))fun;
-        }
-        else if (strcmp((const char *)propValue, "canuse") == 0) {
+        if (strcmp((const char *)propValue, "canuse") == 0) {
             itype->canuse =
                 (bool(*)(const struct unit *, const struct item_type *))fun;
         }

From d9d542cfeebf4019f95761c0f7713a83724b1250 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Fri, 3 Mar 2017 19:19:33 +0100
Subject: [PATCH 40/42] we only call these when target is 0, anyway.

---
 src/give.c | 34 ++++++++++++----------------------
 1 file changed, 12 insertions(+), 22 deletions(-)

diff --git a/src/give.c b/src/give.c
index 884c55283..5cd19710e 100644
--- a/src/give.c
+++ b/src/give.c
@@ -148,32 +148,22 @@ int give_quota(const unit * src, const unit * dst, const item_type * type,
     return n;
 }
 
-static int
-give_horses(unit * s, unit * d, const item_type * itype, int n,
-    struct order *ord)
+static void
+give_horses(unit * s, const item_type * itype, int n)
 {
-    if (d == NULL) {
-        region *r = s->region;
-        if (r->land) {
-            rsethorses(r, rhorses(r) + n);
-        }
-        return 0;
+    region *r = s->region;
+    if (r->land) {
+        rsethorses(r, rhorses(r) + n);
     }
-    return -1;                    /* use the mechanism */
 }
 
-static int
-give_money(unit * s, unit * d, const item_type * itype, int n,
-    struct order *ord)
+static void
+give_money(unit * s, const item_type * itype, int n)
 {
-    if (d == NULL) {
-        region *r = s->region;
-        if (r->land) {
-            rsetmoney(r, rmoney(r) + n);
-        }
-        return 0;
+    region *r = s->region;
+    if (r->land) {
+        rsetmoney(r, rmoney(r) + n);
     }
-    return -1;                    /* use the mechanism */
 }
 
 int
@@ -238,11 +228,11 @@ struct order *ord)
             /* return horses to the region */
             if (itype->construction && itype->flags & ITF_ANIMAL) {
                 if (itype->construction->skill == SK_HORSE_TRAINING) {
-                    give_horses(src, dest, itype, n, ord);
+                    give_horses(src, itype, n);
                 }
             }
             else if (itype->rtype == get_resourcetype(R_SILVER)) {
-                give_money(src, dest, itype, n, ord);
+                give_money(src, itype, n);
             }
         }
         handle_event(src->attribs, "give", dest);

From 50bdc71c1b9f359dc2b8bbbfb5d0840be4e05c06 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sat, 4 Mar 2017 17:27:13 +0100
Subject: [PATCH 41/42] kill lua_canuse_item and itype->canuse

---
 res/core/armor/plate.xml          |  1 -
 res/core/weapons/axe.xml          |  1 -
 res/core/weapons/greatbow.xml     |  3 +--
 res/core/weapons/greatsword.xml   |  1 -
 res/core/weapons/halberd.xml      |  1 -
 res/core/weapons/lance.xml        |  1 -
 res/core/weapons/mallornlance.xml |  1 -
 res/core/weapons/rep_crossbow.xml | 16 ----------------
 res/core/weapons/rustyaxe.xml     |  1 -
 res/core/weapons/rustyhalberd.xml |  1 -
 res/e3a/armor/plate.xml           |  1 -
 res/e3a/armor/scale.xml           |  1 -
 res/e3a/armor/towershield.xml     |  1 -
 res/e3a/weapons/axe.xml           |  1 -
 res/e3a/weapons/greatbow.xml      |  1 -
 res/e3a/weapons/halberd.xml       |  1 -
 res/e3a/weapons/lance.xml         |  1 -
 res/e3a/weapons/mallornlance.xml  |  1 -
 res/e3a/weapons/rustyhalberd.xml  |  1 -
 scripts/eressea/e3/rules.lua      | 28 ----------------------------
 scripts/eressea/resources.lua     |  7 -------
 src/battle.c                      | 12 +-----------
 src/helpers.c                     | 30 ------------------------------
 src/kernel/item.h                 |  3 ---
 src/kernel/xmlreader.c            |  8 +-------
 25 files changed, 3 insertions(+), 121 deletions(-)
 delete mode 100644 res/core/weapons/rep_crossbow.xml

diff --git a/res/core/armor/plate.xml b/res/core/armor/plate.xml
index a22064e90..97d855a73 100644
--- a/res/core/armor/plate.xml
+++ b/res/core/armor/plate.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0"?>
 <resource name="plate">
   <item weight="400" score="150">
-    <function name="canuse" value="lua_canuse_item"/>
     <construction skill="armorer" minskill="4">
       <requirement type="iron" quantity="5"/>
     </construction>
diff --git a/res/core/weapons/axe.xml b/res/core/weapons/axe.xml
index 80a872ad7..3f97a5b8a 100644
--- a/res/core/weapons/axe.xml
+++ b/res/core/weapons/axe.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0"?>
 <resource name="axe">
   <item weight="200">
-    <function name="canuse" value="lua_canuse_item"/>
     <construction skill="weaponsmithing" minskill="3">
       <requirement type="log" quantity="1"/>
       <requirement type="iron" quantity="1"/>
diff --git a/res/core/weapons/greatbow.xml b/res/core/weapons/greatbow.xml
index 14734a129..32f07a41e 100644
--- a/res/core/weapons/greatbow.xml
+++ b/res/core/weapons/greatbow.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0"?>
 <resource name="greatbow">
-  <item weight="100" allow="elf">
-    <function name="canuse" value="lua_canuse_item"/>
+  <item weight="100">
     <construction skill="weaponsmithing" minskill="5">
       <modifier function="mod_elves_only"/>
       <requirement type="mallorn" quantity="2"/>
diff --git a/res/core/weapons/greatsword.xml b/res/core/weapons/greatsword.xml
index cd48b8296..56285f6af 100644
--- a/res/core/weapons/greatsword.xml
+++ b/res/core/weapons/greatsword.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0"?>
 <resource name="greatsword">
   <item weight="200" score="30">
-    <function name="canuse" value="lua_canuse_item"/>
     <construction skill="weaponsmithing" minskill="4">
       <requirement type="iron" quantity="2"/>
     </construction>
diff --git a/res/core/weapons/halberd.xml b/res/core/weapons/halberd.xml
index 5025f4793..7abc86902 100644
--- a/res/core/weapons/halberd.xml
+++ b/res/core/weapons/halberd.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0"?>
 <resource name="halberd">
   <item weight="200">
-    <function name="canuse" value="lua_canuse_item"/>
     <construction skill="weaponsmithing" minskill="3">
       <requirement type="log" quantity="2"/>
       <requirement type="iron" quantity="1"/>
diff --git a/res/core/weapons/lance.xml b/res/core/weapons/lance.xml
index abbb7f31f..4a02bc06f 100644
--- a/res/core/weapons/lance.xml
+++ b/res/core/weapons/lance.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0"?>
 <resource name="lance">
   <item weight="200">
-    <function name="canuse" value="lua_canuse_item"/>
     <construction skill="weaponsmithing" minskill="2">
       <requirement type="log" quantity="2"/>
     </construction>
diff --git a/res/core/weapons/mallornlance.xml b/res/core/weapons/mallornlance.xml
index 0186143e4..c67390d52 100644
--- a/res/core/weapons/mallornlance.xml
+++ b/res/core/weapons/mallornlance.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0"?>
 <resource name="mallornlance">
   <item weight="100">
-    <function name="canuse" value="lua_canuse_item"/>
     <construction skill="weaponsmithing" minskill="5">
       <requirement type="mallorn" quantity="2"/>
     </construction>
diff --git a/res/core/weapons/rep_crossbow.xml b/res/core/weapons/rep_crossbow.xml
deleted file mode 100644
index d6f869046..000000000
--- a/res/core/weapons/rep_crossbow.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0"?>
-<resource name="rep_crossbow">
-  <item weight="100" allow="dwarf halfling">
-    <function name="canuse" value="lua_canuse_item"/>
-    <construction skill="weaponsmithing" minskill="5">
-      <modifier function="mod_dwarves_only"/>
-      <requirement type="log" quantity="1"/>
-      <requirement type="iron" quantity="1"/>
-    </construction>
-    <weapon armorpiercing="true" pierce="true" missile="true" skill="crossbow" offmod="0" defmod="0" reload="1">
-      <damage type="rider" value="3d4+5"/>
-      <damage type="footman" value="3d4+5"/>
-      <modifier type="missile_target" value="0"/>
-    </weapon>
-  </item>
-</resource>
diff --git a/res/core/weapons/rustyaxe.xml b/res/core/weapons/rustyaxe.xml
index 2ff19570c..fea6b8642 100644
--- a/res/core/weapons/rustyaxe.xml
+++ b/res/core/weapons/rustyaxe.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0"?>
 <resource name="rustyaxe">
   <item weight="200">
-    <function name="canuse" value="lua_canuse_item"/>
     <construction skill="weaponsmithing" minskill="3">
       <requirement type="log" quantity="1"/>
       <requirement type="iron" quantity="1"/>
diff --git a/res/core/weapons/rustyhalberd.xml b/res/core/weapons/rustyhalberd.xml
index c1b1f69a9..3f0bd93b7 100644
--- a/res/core/weapons/rustyhalberd.xml
+++ b/res/core/weapons/rustyhalberd.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0"?>
 <resource name="rustyhalberd">
   <item weight="200" score="20">
-    <function name="canuse" value="lua_canuse_item"/>
     <construction skill="weaponsmithing" minskill="3">
       <requirement type="iron" quantity="1"/>
       <requirement type="log" quantity="1"/>
diff --git a/res/e3a/armor/plate.xml b/res/e3a/armor/plate.xml
index 9aeafc3c1..79391fbcb 100644
--- a/res/e3a/armor/plate.xml
+++ b/res/e3a/armor/plate.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0"?>
 <resource name="plate">
   <item weight="400" score="150" deny="goblin">
-    <function name="canuse" value="lua_canuse_item"/>
     <construction skill="armorer" minskill="4">
       <requirement type="iron" quantity="4"/>
     </construction>
diff --git a/res/e3a/armor/scale.xml b/res/e3a/armor/scale.xml
index 691ce5ba7..1038f4d6c 100644
--- a/res/e3a/armor/scale.xml
+++ b/res/e3a/armor/scale.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0"?>
 <resource name="scale">
   <item weight="300" score="150" allow="dwarf halfling">
-    <function name="canuse" value="lua_canuse_item"/>
     <construction skill="armorer" minskill="5">
       <modifier function="mod_dwarves_only"/>
       <requirement type="iron" quantity="2"/>
diff --git a/res/e3a/armor/towershield.xml b/res/e3a/armor/towershield.xml
index 78e113e2a..38e4f0928 100644
--- a/res/e3a/armor/towershield.xml
+++ b/res/e3a/armor/towershield.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0"?>
 <resource name="towershield">
   <item weight="200" score="60" allow="dwarf">
-    <function name="canuse" value="lua_canuse_item"/>
     <construction skill="armorer" minskill="4">
       <modifier function="mod_dwarves_only"/>
       <requirement type="iron" quantity="1"/>
diff --git a/res/e3a/weapons/axe.xml b/res/e3a/weapons/axe.xml
index 991a2b504..6066c4cfc 100644
--- a/res/e3a/weapons/axe.xml
+++ b/res/e3a/weapons/axe.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0"?>
 <resource name="axe">
   <item weight="200" deny="goblin">
-    <function name="canuse" value="lua_canuse_item"/>
     <construction skill="weaponsmithing" minskill="3">
       <requirement type="log" quantity="1"/>
       <requirement type="iron" quantity="1"/>
diff --git a/res/e3a/weapons/greatbow.xml b/res/e3a/weapons/greatbow.xml
index b488a011c..745d1793d 100644
--- a/res/e3a/weapons/greatbow.xml
+++ b/res/e3a/weapons/greatbow.xml
@@ -5,7 +5,6 @@
 -->
 <resource name="greatbow">
   <item weight="100" allow="elf">
-    <function name="canuse" value="lua_canuse_item"/>
     <construction skill="weaponsmithing" minskill="5">
       <modifier function="mod_elves_only"/>
       <requirement type="mallorn" quantity="2"/>
diff --git a/res/e3a/weapons/halberd.xml b/res/e3a/weapons/halberd.xml
index c07cbfd4d..c22e020b4 100644
--- a/res/e3a/weapons/halberd.xml
+++ b/res/e3a/weapons/halberd.xml
@@ -4,7 +4,6 @@
 -->
 <resource name="halberd">
   <item weight="200" deny="goblin">
-    <function name="canuse" value="lua_canuse_item"/>
     <construction skill="weaponsmithing" minskill="3">
       <requirement type="log" quantity="2"/>
       <requirement type="iron" quantity="1"/>
diff --git a/res/e3a/weapons/lance.xml b/res/e3a/weapons/lance.xml
index a3f641833..285862484 100644
--- a/res/e3a/weapons/lance.xml
+++ b/res/e3a/weapons/lance.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0"?>
 <resource name="lance">
   <item weight="200" deny="goblin">
-    <function name="canuse" value="lua_canuse_item"/>
     <construction skill="weaponsmithing" minskill="2">
       <requirement type="log" quantity="2"/>
     </construction>
diff --git a/res/e3a/weapons/mallornlance.xml b/res/e3a/weapons/mallornlance.xml
index d2aacaea7..8eb25ceb4 100644
--- a/res/e3a/weapons/mallornlance.xml
+++ b/res/e3a/weapons/mallornlance.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0"?>
 <resource name="mallornlance">
   <item weight="100" deny="goblin">
-    <function name="canuse" value="lua_canuse_item"/>
     <construction skill="weaponsmithing" minskill="5">
       <requirement type="mallorn" quantity="2"/>
     </construction>
diff --git a/res/e3a/weapons/rustyhalberd.xml b/res/e3a/weapons/rustyhalberd.xml
index 32c0d4497..a53aec515 100644
--- a/res/e3a/weapons/rustyhalberd.xml
+++ b/res/e3a/weapons/rustyhalberd.xml
@@ -4,7 +4,6 @@
 -->
 <resource name="rustyhalberd">
   <item weight="200" score="20" deny="goblin">
-    <function name="canuse" value="lua_canuse_item"/>
     <construction skill="weaponsmithing" minskill="3">
       <requirement type="iron" quantity="1"/>
       <requirement type="log" quantity="1"/>
diff --git a/scripts/eressea/e3/rules.lua b/scripts/eressea/e3/rules.lua
index c0ac8978a..e7a54ff41 100644
--- a/scripts/eressea/e3/rules.lua
+++ b/scripts/eressea/e3/rules.lua
@@ -1,31 +1,3 @@
--- when appending to this, make sure the item has a canuse-function!
-local goblin_denied = " plate lance mallornlance greatbow axe greatsword halberd rustyaxe rustyhalberd towershield scale "
-function item_canuse(u, iname)
-  local race = u.race
-  if race=="goblin" then
-    if string.find(goblin_denied, " " .. iname .. " ") then
-      return false
-    end
-  end
-  if iname=="rep_crossbow" then
-    -- only dwarves and halflings allowed to use repeating crossbow
-    return race=="dwarf" or race=="halfling"
-  end
-  if iname=="scale" then
-    -- only dwarves and halflings can use scale
-    return race=="dwarf" or race=="halfling"
-  end
-  if iname=="towershield" then 
-    -- only dwarves allowed to use towershield
-    return race=="dwarf"
-  end
-  if iname=="greatbow" then
-    -- only elves use greatbow
-    return race=="elf"
-  end
-  return true
-end
-
 function building_taxes(b, blevel)
   btype = b.type
   if btype=="castle" then
diff --git a/scripts/eressea/resources.lua b/scripts/eressea/resources.lua
index 2829d00c7..588620aa4 100644
--- a/scripts/eressea/resources.lua
+++ b/scripts/eressea/resources.lua
@@ -1,12 +1,5 @@
 -- global functions used in items.xml
 
-if not item_canuse then
-    -- define a default, everyone can use everything
-    function item_canuse(u, iname)
-        return true
-    end
-end
-
 function peasant_getresource(u)
   return u.region:get_resource("peasant")
 end
diff --git a/src/battle.c b/src/battle.c
index fa5725c50..e8eb426e3 100644
--- a/src/battle.c
+++ b/src/battle.c
@@ -576,17 +576,7 @@ static weapon *select_weapon(const troop t, bool attacking,
 
 static bool i_canuse(const unit * u, const item_type * itype)
 {
-    bool result = true;
-    if (itype->canuse) {
-        result = itype->canuse(u, itype);
-    }
-    if (result!=rc_can_use(u_race(u), itype)) {
-        log_error("conversion error: %s should be %s to use %s",
-            u->_race->_name,
-            result ? "allowed" : "forbidden",
-            itype->rtype->_name);
-    }
-    return result;
+    return rc_can_use(u_race(u), itype);
 }
 
 static int
diff --git a/src/helpers.c b/src/helpers.c
index e8e6521c4..a4746461b 100644
--- a/src/helpers.c
+++ b/src/helpers.c
@@ -320,34 +320,6 @@ static int lua_getresource(unit * u, const struct resource_type *rtype)
     return result;
 }
 
-static bool lua_canuse_item(const unit * u, const struct item_type *itype)
-{
-    bool result = true;
-    lua_State *L = (lua_State *)global.vm_state;
-    const char *fname = "item_canuse";
-
-    lua_getglobal(L, fname);
-    if (lua_isfunction(L, -1)) {
-        tolua_pushusertype(L, (void *)u, TOLUA_CAST "unit");
-        tolua_pushstring(L, itype->rtype->_name);
-
-        if (lua_pcall(L, 2, 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 = lua_toboolean(L, -1);
-            lua_pop(L, 1);
-        }
-    }
-    else {
-        log_error("use(%s) calling '%s': not a function.\n", unitname(u), fname);
-        lua_pop(L, 1);
-    }
-    return result;
-}
-
 static int
 lua_wage(const region * r, const faction * f, const race * rc, int in_turn)
 {
@@ -563,8 +535,6 @@ void register_tolua_helpers(void)
         TOLUA_CAST "lua_initfamiliar");
     register_function((pf_generic)lua_getresource,
         TOLUA_CAST "lua_getresource");
-    register_function((pf_generic)lua_canuse_item,
-        TOLUA_CAST "lua_canuse_item");
     register_function((pf_generic)lua_changeresource,
         TOLUA_CAST "lua_changeresource");
     register_function((pf_generic)lua_equipmentcallback,
diff --git a/src/kernel/item.h b/src/kernel/item.h
index 5f8d879f9..dda0cf3e4 100644
--- a/src/kernel/item.h
+++ b/src/kernel/item.h
@@ -125,9 +125,6 @@ extern "C" {
         int mask_deny;
         struct construction *construction;
         char *_appearance[2];       /* wie es f�r andere aussieht */
-        /* --- functions --- */
-        bool(*canuse) (const struct unit * user,
-            const struct item_type * itype);
         int score;
     } item_type;
 
diff --git a/src/kernel/xmlreader.c b/src/kernel/xmlreader.c
index edbaa37f4..aabab91d6 100644
--- a/src/kernel/xmlreader.c
+++ b/src/kernel/xmlreader.c
@@ -873,13 +873,7 @@ static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype)
             continue;
         }
         assert(propValue != NULL);
-        if (strcmp((const char *)propValue, "canuse") == 0) {
-            itype->canuse =
-                (bool(*)(const struct unit *, const struct item_type *))fun;
-        }
-        else {
-            log_error("unknown function type '%s' for item '%s'\n", (const char *)propValue, rtype->_name);
-        }
+        log_error("unknown function type '%s' for item '%s'\n", (const char *)propValue, rtype->_name);
         xmlFree(propValue);
     }
     itype->score = xml_ivalue(node, "score", 0);

From acfb6665390df3a85ad17eb803717d5d31b26421 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sat, 4 Mar 2017 20:59:43 +0100
Subject: [PATCH 42/42] remove rtype->uget funpointer, and lua callbacks. fix a
 missing lua_pop that trashed the heap.

---
 res/core/resources/hp.xml        |  1 -
 res/core/resources/peasant.xml   |  1 -
 res/e3a/weapons.xml              |  2 +-
 res/e3a/weapons/rep_crossbow.xml | 15 +++++++++++++++
 scripts/eressea/resources.lua    |  8 --------
 src/helpers.c                    |  2 +-
 src/kernel/item.h                |  3 ---
 src/kernel/pool.c                | 14 ++++++--------
 src/kernel/region.c              |  3 +++
 src/kernel/save.c                |  2 ++
 src/kernel/xmlreader.c           |  3 ---
 11 files changed, 28 insertions(+), 26 deletions(-)
 create mode 100644 res/e3a/weapons/rep_crossbow.xml

diff --git a/res/core/resources/hp.xml b/res/core/resources/hp.xml
index fb1b6e32f..aa0ad4d29 100644
--- a/res/core/resources/hp.xml
+++ b/res/core/resources/hp.xml
@@ -1,5 +1,4 @@
 <?xml version="1.0"?>
 <resource name="hp" pooled="false">
   <function name="change" value="lua_changeresource"/>
-  <function name="get" value="lua_getresource"/>
 </resource>
diff --git a/res/core/resources/peasant.xml b/res/core/resources/peasant.xml
index 32e1e1ca1..bce23430c 100644
--- a/res/core/resources/peasant.xml
+++ b/res/core/resources/peasant.xml
@@ -1,5 +1,4 @@
 <?xml version="1.0"?>
 <resource name="peasant" pooled="false">
   <function name="change" value="lua_changeresource"/>
-  <function name="get" value="lua_getresource"/>
 </resource>
diff --git a/res/e3a/weapons.xml b/res/e3a/weapons.xml
index 96a987b28..fdc8f1f3a 100644
--- a/res/e3a/weapons.xml
+++ b/res/e3a/weapons.xml
@@ -4,13 +4,13 @@
   <xi:include href="config://core/weapons/catapult.xml"/>
   <xi:include href="config://core/weapons/mallornbow.xml"/>
   <xi:include href="config://core/weapons/mallornspear.xml"/>
-  <xi:include href="config://core/weapons/rep_crossbow.xml"/>
   <xi:include href="config://core/weapons/runesword.xml"/>
   <xi:include href="config://core/weapons/rustyaxe.xml"/>
   <xi:include href="config://core/weapons/rustysword.xml"/>
   <xi:include href="config://core/weapons/spear.xml"/>
   <xi:include href="config://core/weapons/sword.xml"/>
   <xi:include href="config://core/weapons/firesword.xml"/>
+  <xi:include href="config://game/weapons/rep_crossbow.xml"/>
   <xi:include href="config://game/weapons/lance.xml"/>
   <xi:include href="config://game/weapons/axe.xml"/>
   <xi:include href="config://game/weapons/crossbow.xml"/>
diff --git a/res/e3a/weapons/rep_crossbow.xml b/res/e3a/weapons/rep_crossbow.xml
new file mode 100644
index 000000000..02f0d865f
--- /dev/null
+++ b/res/e3a/weapons/rep_crossbow.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+<resource name="rep_crossbow">
+  <item weight="100" allow="dwarf halfling">
+    <construction skill="weaponsmithing" minskill="5">
+      <modifier function="mod_dwarves_only"/>
+      <requirement type="log" quantity="1"/>
+      <requirement type="iron" quantity="1"/>
+    </construction>
+    <weapon armorpiercing="true" pierce="true" missile="true" skill="crossbow" offmod="0" defmod="0" reload="1">
+      <damage type="rider" value="3d4+5"/>
+      <damage type="footman" value="3d4+5"/>
+      <modifier type="missile_target" value="0"/>
+    </weapon>
+  </item>
+</resource>
diff --git a/scripts/eressea/resources.lua b/scripts/eressea/resources.lua
index 588620aa4..fa6e1c4da 100644
--- a/scripts/eressea/resources.lua
+++ b/scripts/eressea/resources.lua
@@ -1,9 +1,5 @@
 -- global functions used in items.xml
 
-function peasant_getresource(u)
-  return u.region:get_resource("peasant")
-end
-
 function peasant_changeresource(u, delta)
   local p = u.region:get_resource("peasant")
   p = p + delta
@@ -14,10 +10,6 @@ function peasant_changeresource(u, delta)
   return p
 end
 
-function hp_getresource(u)
-  return u.hp
-end
-
 function hp_changeresource(u, delta)
   local hp = u.hp + delta
   
diff --git a/src/helpers.c b/src/helpers.c
index a4746461b..402ecdd48 100644
--- a/src/helpers.c
+++ b/src/helpers.c
@@ -496,13 +496,13 @@ use_item_lua(unit *u, const item_type *itype, int amount, struct order *ord)
         }
         return result;
     }
+    lua_pop(L, 1);
     if (itype->rtype->ptype) {
         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);
-    lua_pop(L, 1);
 
     return result;
 }
diff --git a/src/kernel/item.h b/src/kernel/item.h
index dda0cf3e4..77fe1a9c2 100644
--- a/src/kernel/item.h
+++ b/src/kernel/item.h
@@ -68,8 +68,6 @@ extern "C" {
 
     typedef int(*rtype_uchange) (struct unit * user,
         const struct resource_type * rtype, int delta);
-    typedef int(*rtype_uget) (const struct unit * user,
-        const struct resource_type * rtype);
     typedef char *(*rtype_name) (const struct resource_type * rtype, int flags);
     typedef struct resource_type {
         /* --- constants --- */
@@ -77,7 +75,6 @@ extern "C" {
         unsigned int flags;
         /* --- functions --- */
         rtype_uchange uchange;
-        rtype_uget uget;
         rtype_name name;
         struct rawmaterial_type *raw;
         struct resource_mod *modifiers;
diff --git a/src/kernel/pool.c b/src/kernel/pool.c
index 702f86e0f..8dfd589c3 100644
--- a/src/kernel/pool.c
+++ b/src/kernel/pool.c
@@ -40,11 +40,11 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 int get_resource(const unit * u, const resource_type * rtype)
 {
     assert(rtype);
-    if (rtype->uget) {
-        /* this resource is probably special */
-        int i = rtype->uget(u, rtype);
-        if (i >= 0)
-            return i;
+    if (rtype == get_resourcetype(R_PEASANT)) {
+        return u->region->land ? u->region->land->peasants : 0;
+    }
+    else if (rtype == rt_find("hp")) {
+        return u->hp;
     }
     else if (rtype->uchange) {
         /* this resource is probably special */
@@ -176,7 +176,7 @@ int count)
     }
     if (rtype->flags & RTF_POOLED && mode & ~(GET_SLACK | GET_RESERVE)) {
         for (v = r->units; v && use < count; v = v->next)
-            if (u != v && (v->items || rtype->uget)) {
+            if (u != v) {
                 int mask;
 
                 if ((u_race(v)->ec_flags & ECF_KEEP_ITEM))
@@ -234,8 +234,6 @@ use_pooled(unit * u, const resource_type * rtype, unsigned int mode, int count)
                 int mask;
                 if ((u_race(v)->ec_flags & ECF_KEEP_ITEM))
                     continue;
-                if (v->items == NULL && rtype->uget == NULL)
-                    continue;
 
                 if (v->faction == f) {
                     mask = (mode >> 3) & (GET_SLACK | GET_RESERVE);
diff --git a/src/kernel/region.c b/src/kernel/region.c
index aefffa875..e4f33db33 100644
--- a/src/kernel/region.c
+++ b/src/kernel/region.c
@@ -38,6 +38,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include "unit.h"
 
 /* util includes */
+#include <util/assert.h>
 #include <util/attrib.h>
 #include <util/bsdstring.h>
 #include <util/gamedata.h>
@@ -697,6 +698,7 @@ void r_setdemand(region * r, const luxury_type * ltype, int value)
     d = *dp;
     if (!d) {
         d = *dp = malloc(sizeof(struct demand));
+        assert_alloc(d);
         d->next = NULL;
         d->type = ltype;
     }
@@ -768,6 +770,7 @@ region *new_region(int x, int y, struct plane *pl, int uid)
         return r;
     }
     r = calloc(1, sizeof(region));
+    assert_alloc(r);
     r->x = x;
     r->y = y;
     r->uid = uid;
diff --git a/src/kernel/save.c b/src/kernel/save.c
index 8316121f3..e487737f9 100644
--- a/src/kernel/save.c
+++ b/src/kernel/save.c
@@ -52,6 +52,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include <triggers/timeout.h>
 
 /* util includes */
+#include <util/assert.h>
 #include <util/attrib.h>
 #include <util/base36.h>
 #include <util/bsdstring.h>
@@ -646,6 +647,7 @@ unit *read_unit(struct gamedata *data)
     }
     else {
         u = calloc(sizeof(unit), 1);
+        assert_alloc(u);
         u->no = n;
         uhash(u);
     }
diff --git a/src/kernel/xmlreader.c b/src/kernel/xmlreader.c
index aabab91d6..4db528853 100644
--- a/src/kernel/xmlreader.c
+++ b/src/kernel/xmlreader.c
@@ -972,9 +972,6 @@ static int parse_resources(xmlDocPtr doc)
                 if (strcmp((const char *)propValue, "change") == 0) {
                     rtype->uchange = (rtype_uchange)fun;
                 }
-                else if (strcmp((const char *)propValue, "get") == 0) {
-                    rtype->uget = (rtype_uget)fun;
-                }
                 else if (strcmp((const char *)propValue, "name") == 0) {
                     rtype->name = (rtype_name)fun;
                 }