From a4a3ebd633012d5977ee621fad00c953d661231e Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno@kn-bremen.de>
Date: Sat, 21 Jul 2018 21:41:13 +0200
Subject: [PATCH 1/6] backup the reports, because sometimes I am an idiot

---
 process/backup-eressea | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/process/backup-eressea b/process/backup-eressea
index 4914f5af0..4e5f69002 100755
--- a/process/backup-eressea
+++ b/process/backup-eressea
@@ -28,6 +28,9 @@ if [ -e orders.$TURN ]; then
 files="$files orders.$TURN"
 fi
 echo "backup turn $TURN, game $GAME, files: $files"
+if [ -d reports ] ; then
+  tar cjf backup/$TURN-reports.tar.bz2 reports
+fi
 tar cjf backup/$TURN.tar.bz2 $files
 echo "uploading game-$GAME/$TURN.tar.bz2"
 curl -s -n -T backup/$TURN.tar.bz2 https://dav.box.com/dav/Eressea/game-$GAME/$TURN.tar.bz2

From e7184add00e0d1e1de1629489bddd5537e88f023 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno@kn-bremen.de>
Date: Tue, 24 Jul 2018 13:57:46 +0200
Subject: [PATCH 2/6] use system-wide muttrc

---
 process/send-bz2-report | 2 +-
 process/send-zip-report | 2 +-
 s/preview               | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/process/send-bz2-report b/process/send-bz2-report
index c9fb87840..51a8113bf 100755
--- a/process/send-bz2-report
+++ b/process/send-bz2-report
@@ -25,6 +25,6 @@ addr=$1
 subj=$2
 shift 2
 
-mutt -F "$ERESSEA/etc/muttrc" -s "$subj" -a "$@" -- "$addr" \
+mutt -s "$subj" -a "$@" -- "$addr" \
 	< "$ERESSEA/server/etc/$TEMPLATE"
 
diff --git a/process/send-zip-report b/process/send-zip-report
index 89f742b82..a6bd85246 100755
--- a/process/send-zip-report
+++ b/process/send-zip-report
@@ -44,6 +44,6 @@ addr=$1
 subject=$2
 shift 2
 
-mutt -F "$ERESSEA/etc/muttrc" -s "$subject" -a "$@" -- "$addr" \
+mutt -s "$subject" -a "$@" -- "$addr" \
 	< "$TEMPLATE" || echo "Sending failed for email/report: $2/$3"
 
diff --git a/s/preview b/s/preview
index 68fe6e39f..39d5466d0 100755
--- a/s/preview
+++ b/s/preview
@@ -93,7 +93,7 @@ email=$(grep "faction=$1:" reports.txt | cut -d: -f2 | sed 's/email=//')
 echo "sending reports to $1 / $email"
 info=/dev/null
 [ -e ../email.txt ] && info=../email.txt
-cat $info | mutt -F $ERESSEA/etc/muttrc -s "Testauswertung Spiel $game Partei $1" -a $zip -- $email
+cat $info | mutt -s "Testauswertung Spiel $game Partei $1" -a $zip -- $email
 }
 
 game=0

From ec571924037cc06b23eff622b828f865cd65a57b Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Mon, 30 Jul 2018 22:19:40 +0200
Subject: [PATCH 3/6] equip_newunits got called twice.

---
 scripts/eressea/equipment.lua |  1 -
 scripts/tests/config.lua      | 21 +++++++++++++++++++++
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/scripts/eressea/equipment.lua b/scripts/eressea/equipment.lua
index 8852dfb27..c7bfe7dad 100644
--- a/scripts/eressea/equipment.lua
+++ b/scripts/eressea/equipment.lua
@@ -2,7 +2,6 @@
 local self = {}
 
 local function equip_first(u)
-    equip_newunits(u)
     name = 'seed_' .. u.race
     equip_unit(u, name, 255)
 end
diff --git a/scripts/tests/config.lua b/scripts/tests/config.lua
index 316a8ccca..733a8ee07 100644
--- a/scripts/tests/config.lua
+++ b/scripts/tests/config.lua
@@ -30,6 +30,26 @@ function test_first_troll()
     assert_equal(2, u:eff_skill('perception'))
 end
 
+function test_first_human()
+    local f = faction.create('human')
+    local r = region.create(0, 0, "plain")
+    local u = unit.create(f, r, 1)
+    u:equip('first_unit')
+    assert_not_nil(u.building)
+    assert_equal('castle', u.building.type)
+    assert_equal(10, u.building.size)
+end
+
+function test_first_aquarian()
+    local f = faction.create('aquarian')
+    local r = region.create(0, 0, "plain")
+    local u = unit.create(f, r, 1)
+    u:equip('first_unit')
+    assert_not_nil(u.ship)
+    assert_equal('boat', u.ship.type)
+    assert_equal(1, u:get_skill('sailing'))
+end
+
 function test_seed_unit()
     local r = region.create(0, 0, "plain")
     local f = faction.create('human')
@@ -53,3 +73,4 @@ function test_seed_elf()
     assert_equal('castle', u.building.type)
     assert_equal(10, u.building.size)
 end
+

From 6f5f1651c7a1f86358929c1ebe404f310f5e6e17 Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sun, 9 Sep 2018 07:41:30 +0200
Subject: [PATCH 4/6] fix crashes when trying to trade on ocean. fix missing
 message sections.

---
 res/core/messages.xml | 6 +++---
 src/economy.c         | 5 +++--
 src/kernel/region.c   | 5 ++++-
 3 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/res/core/messages.xml b/res/core/messages.xml
index d566c82e6..7e55e05f9 100644
--- a/res/core/messages.xml
+++ b/res/core/messages.xml
@@ -30,7 +30,7 @@
     </type>
   </message>
 
-  <message name="target_region_invalid">
+  <message name="target_region_invalid" section="errors">
     <type>
       <arg name="unit" type="unit"/>
       <arg name="region" type="region"/>
@@ -38,7 +38,7 @@
     </type>
   </message>
   
-  <message name="missing_direction">
+  <message name="missing_direction" section="errors">
     <type>
       <arg name="unit" type="unit"/>
       <arg name="region" type="region"/>
@@ -46,7 +46,7 @@
     </type>
   </message>
   
-  <message name="target_region_not_empty">
+  <message name="target_region_not_empty" section="errors">
     <type>
       <arg name="unit" type="unit"/>
       <arg name="region" type="region"/>
diff --git a/src/economy.c b/src/economy.c
index 3bb7b1066..4a6aa5672 100644
--- a/src/economy.c
+++ b/src/economy.c
@@ -1660,7 +1660,7 @@ static void buy(unit * u, econ_request ** buyorders, struct order *ord)
             return;
         }
     }
-    if (r_demand(r, ltype)) {
+    if (!r->land || r_demand(r, ltype)) {
         ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "luxury_notsold", ""));
         return;
     }
@@ -1702,6 +1702,7 @@ static void expandselling(region * r, econ_request * sellorders, int limit)
     static int bt_cache;
     static const struct building_type *castle_bt, *harbour_bt, *caravan_bt;
 
+    assert(r->land);
     if (bt_changed(&bt_cache)) {
         castle_bt = bt_find("castle");
         harbour_bt = bt_find("harbour");
@@ -1971,7 +1972,7 @@ static bool sell(unit * u, econ_request ** sellorders, struct order *ord)
         econ_request *o;
         int k, available;
 
-        if (!r_demand(r, ltype)) {
+        if (!r->land || !r_demand(r, ltype)) {
             cmistake(u, ord, 263, MSG_COMMERCE);
             return false;
         }
diff --git a/src/kernel/region.c b/src/kernel/region.c
index 7cb52c201..29f7222e1 100644
--- a/src/kernel/region.c
+++ b/src/kernel/region.c
@@ -716,7 +716,10 @@ const item_type *r_luxury(const region * r)
 
 int r_demand(const region * r, const luxury_type * ltype)
 {
-    struct demand *d = r->land->demands;
+    struct demand *d;
+
+    assert(r && r->land);
+    d = r->land->demands;
     while (d && d->type != ltype)
         d = d->next;
     if (!d)

From 67089aeb8c528412303c416631e94f9541d50a4b Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sun, 9 Sep 2018 15:13:20 +0200
Subject: [PATCH 5/6] Bug 2487: Fix LERNE AUTO translations Also disallow
 autolearning expensive skills.

---
 clibs                           |  2 +-
 res/translations/strings.de.po  |  7 +++++
 res/translations/strings.en.po  |  9 ++++++-
 scripts/tests/e2/e2features.lua | 24 +++++++++++++++++
 scripts/tests/study.lua         | 14 +++++-----
 src/automate.c                  |  8 +++---
 src/kernel/order.c              | 19 +++++++++-----
 src/kernel/order.test.c         | 13 ++++++++++
 src/skill.c                     | 35 +++++++++++++++++++++++++
 src/skill.h                     |  2 ++
 src/skill.test.c                | 26 ++++++++++++++++++-
 src/study.c                     | 46 ++++++++-------------------------
 src/study.h                     | 37 +++++++++++++-------------
 13 files changed, 169 insertions(+), 73 deletions(-)

diff --git a/clibs b/clibs
index 66a891b38..d86c85254 160000
--- a/clibs
+++ b/clibs
@@ -1 +1 @@
-Subproject commit 66a891b383f1a51bb0d4e5cf002530f7f70bf7f4
+Subproject commit d86c8525489d7f11b7ba13e101bb59ecf160b871
diff --git a/res/translations/strings.de.po b/res/translations/strings.de.po
index 1731fa10e..f4b166221 100644
--- a/res/translations/strings.de.po
+++ b/res/translations/strings.de.po
@@ -2864,6 +2864,9 @@ msgstr "der Schatten"
 msgid "ALLES"
 msgstr "ALLES"
 
+msgid "AUTO"
+msgstr "AUTO"
+
 msgid "undead_postfix_2"
 msgstr "der Finsternis"
 
@@ -5958,6 +5961,10 @@ msgctxt "keyword"
 msgid "maketemp"
 msgstr "MACHE TEMP"
 
+msgctxt "keyword"
+msgid "autostudy"
+msgstr "LERNE AUTO"
+
 msgctxt "spell"
 msgid "reanimate"
 msgstr "Wiederbelebung"
diff --git a/res/translations/strings.en.po b/res/translations/strings.en.po
index 0d64e9f99..32924310c 100644
--- a/res/translations/strings.en.po
+++ b/res/translations/strings.en.po
@@ -2510,6 +2510,9 @@ msgstr "halfling foot"
 msgid "ALLES"
 msgstr "ALL"
 
+msgid "AUTO"
+msgstr "AUTO"
+
 msgctxt "race"
 msgid "songdragon_d"
 msgstr "song dragons"
@@ -5268,7 +5271,11 @@ msgstr "berserkers blood potions"
 
 msgctxt "keyword"
 msgid "maketemp"
-msgstr "MAKETEMP"
+msgstr "MAKE TEMP"
+
+msgctxt "keyword"
+msgid "autostudy"
+msgstr "LEARN AUTO"
 
 msgctxt "spell"
 msgid "reanimate"
diff --git a/scripts/tests/e2/e2features.lua b/scripts/tests/e2/e2features.lua
index bf90ab6a0..54033d496 100644
--- a/scripts/tests/e2/e2features.lua
+++ b/scripts/tests/e2/e2features.lua
@@ -12,6 +12,30 @@ function setup()
     eressea.settings.set("rules.peasants.growth.factor", "0")
 end
 
+function test_study_auto()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("human")
+    local u = unit.create(f, r, 1)
+    u:add_order("LERN AUT Waffenbau")
+    assert_equal("LERNE AUTO Waffenbau", u:get_order(0))
+    process_orders()
+    assert_equal(1, u:get_skill("weaponsmithing"))
+end
+
+function test_study_auto_expensive()
+    local r = region.create(0, 0, "plain")
+    local f = faction.create("human")
+    local u = unit.create(f, r, 1)
+    u:add_order("LERNE AUTO Magie")
+    assert_equal("LERNE Magie", u:get_order(0))
+    u:clear_orders()
+    u:add_order("LERN AUT Taktik")
+    assert_equal("LERNE Taktik", u:get_order(0))
+    u:clear_orders()
+    u:add_order("LERN AUT Waffenbau")
+    assert_equal("LERNE AUTO Waffenbau", u:get_order(0))
+end
+
 function test_calendar()
     assert_equal("winter", get_season(1011))
     assert_equal("spring", get_season(1012))
diff --git a/scripts/tests/study.lua b/scripts/tests/study.lua
index 14d4ce1d6..32638ba90 100644
--- a/scripts/tests/study.lua
+++ b/scripts/tests/study.lua
@@ -24,7 +24,7 @@ end
 
 function test_study()
     local r = region.create(0, 0, "plain")
-    local f = faction.create("human", "test@example.com", "de")
+    local f = faction.create("human")
     local u = unit.create(f, r, 1)
     u:add_order("LERNEN Armbrust")
     process_orders()
@@ -33,7 +33,7 @@ end
 
 function test_study_expensive()
     local r = region.create(0, 0, "plain")
-    local f = faction.create("human", "test@example.com", "de")
+    local f = faction.create("human")
     local u = unit.create(f, r, 1)
     eressea.settings.set("skills.cost.alchemy", "50")
     u:add_order("LERNEN Alchemie")
@@ -45,7 +45,7 @@ end
 
 function test_unit_spells()
     local r = region.create(0, 0, "plain")
-    local f = faction.create("human", "test@example.com", "de")
+    local f = faction.create("human")
     local u = unit.create(f, r, 1)
     u.magic = "gray"
     u:set_skill("magic", 1)
@@ -75,7 +75,7 @@ end
 
 function test_study_no_teacher()
     local r = region.create(0, 0, "plain")
-    local f = faction.create("human", "test@example.com", "de")
+    local f = faction.create("human")
     local u1 = make_student(f, r, 1)
     u1:set_skill("crossbow", 1)
     process_orders()
@@ -84,7 +84,7 @@ end
 
 function test_study_with_teacher()
     local r = region.create(0, 0, "plain")
-    local f = faction.create("human", "test@example.com", "de")
+    local f = faction.create("human")
     local u1 = make_student(f, r, 1)
 
     make_teacher(u1)
@@ -95,7 +95,7 @@ end
 
 function test_study_too_many_students()
     local r = region.create(0, 0, "plain")
-    local f = faction.create("human", "test@example.com", "de")
+    local f = faction.create("human")
     local u1 = make_student(f, r, 20, "Taktik")
     u1.name = "Student"
     u1:add_item("money", 201*u1.number)
@@ -106,7 +106,7 @@ end
 
 function test_study_multiple_teachers()
     local r = region.create(0, 0, "plain")
-    local f = faction.create("human", "test@example.com", "de")
+    local f = faction.create("human")
     local u1 = make_student(f, r, 20, "Taktik")
     u1.name = "Student"
     u1:add_item("money", 201*u1.number)
diff --git a/src/automate.c b/src/automate.c
index a74cf770b..6af40d649 100644
--- a/src/automate.c
+++ b/src/automate.c
@@ -38,14 +38,14 @@ int autostudy_init(scholar scholars[], int max_scholars, region *r)
         if (kwd == K_AUTOSTUDY) {
             if (long_order_allowed(u) && unit_can_study(u)) {
                 scholar * st = scholars + nscholars;
-                if (++nscholars == max_scholars) {
-                    log_fatal("you must increase MAXSCHOLARS");
-                }
-                st->u = u;
                 init_order(u->thisorder, u->faction->locale);
                 st->sk = getskill(u->faction->locale);
                 st->level = effskill_study(u, st->sk);
                 st->learn = 0;
+                st->u = u;
+                if (++nscholars == max_scholars) {
+                    log_fatal("you must increase MAXSCHOLARS");
+                }
             }
             else {
                 ADDMSG(&u->faction->msgs, msg_feedback(u, u->thisorder, "error_race_nolearn", "race",
diff --git a/src/kernel/order.c b/src/kernel/order.c
index 342f45577..c14a2b30b 100644
--- a/src/kernel/order.c
+++ b/src/kernel/order.c
@@ -141,7 +141,8 @@ int stream_order(struct stream *out, const struct order *ord, const struct local
     if (ord->id < 0) {
         skill_t sk = (skill_t)(100 + ord->id);
 
-        assert(kwd == K_STUDY && sk != SK_MAGIC && sk < MAXSKILLS);
+        assert(kwd == K_AUTOSTUDY || kwd == K_STUDY);
+        assert(sk != SK_MAGIC && sk < MAXSKILLS);
         text = skillname(sk, lang);
         if (strchr(text, ' ') != NULL) {
             swrite(" '", 1, 2, out);
@@ -310,12 +311,13 @@ order *parse_order(const char *s, const struct locale * lang)
     assert(lang);
     assert(s);
     if (*s != 0) {
+        char token[32];
         keyword_t kwd = NOKEYWORD;
         const char *sptr = s;
         bool persistent = false, noerror = false;
-        const char * p;
+        char * p;
 
-        p = *sptr ? parse_token_depr(&sptr) : 0;
+        p = parse_token(&sptr, token, sizeof(token));
         if (p) {
             while (*p == '!' || *p == '@') {
                 if (*p == '!') noerror = true;
@@ -326,7 +328,7 @@ order *parse_order(const char *s, const struct locale * lang)
         }
         if (kwd == K_MAKE) {
             const char *sp = sptr;
-            p = parse_token_depr(&sp);
+            p = parse_token(&sp, token, sizeof(token));
             if (p && isparam(p, lang, P_TEMP)) {
                 kwd = K_MAKETEMP;
                 sptr = sp;
@@ -334,10 +336,15 @@ order *parse_order(const char *s, const struct locale * lang)
         }
         else if (kwd == K_STUDY) {
             const char *sp = sptr;
-            p = parse_token_depr(&sp);
+            p = parse_token(&sp, token, sizeof(token));
             if (p && isparam(p, lang, P_AUTO)) {
-                kwd = K_AUTOSTUDY;
+                skill_t sk;
                 sptr = sp;
+                p = parse_token(&sp, token, sizeof(token));
+                sk = get_skill(p, lang);
+                if (!expensive_skill(sk)) {
+                    kwd = K_AUTOSTUDY;
+                }
             }
         }
         if (kwd != NOKEYWORD) {
diff --git a/src/kernel/order.test.c b/src/kernel/order.test.c
index d1f8b4176..c4a55177b 100644
--- a/src/kernel/order.test.c
+++ b/src/kernel/order.test.c
@@ -121,6 +121,8 @@ static void test_parse_autostudy(CuTest *tc) {
     test_setup();
     lang = get_or_create_locale("en");
     locale_setstring(lang, mkname("skill", skillnames[SK_ENTERTAINMENT]), "Entertainment");
+    locale_setstring(lang, mkname("skill", skillnames[SK_MAGIC]), "Magic");
+    locale_setstring(lang, mkname("skill", skillnames[SK_TACTICS]), "Tactics");
     locale_setstring(lang, keyword(K_STUDY), "STUDY");
     locale_setstring(lang, keyword(K_AUTOSTUDY), "AUTOSTUDY");
     locale_setstring(lang, parameters[P_AUTO], "AUTO");
@@ -134,6 +136,17 @@ static void test_parse_autostudy(CuTest *tc) {
     CuAssertIntEquals(tc, K_AUTOSTUDY, init_order(ord, lang));
     CuAssertStrEquals(tc, "Entertainment", getstrtoken());
     free_order(ord);
+
+    ord = parse_order("STUDY AUTO Magic", lang);
+    CuAssertIntEquals(tc, K_STUDY, getkeyword(ord));
+    CuAssertStrEquals(tc, "STUDY Magic", get_command(ord, lang, cmd, sizeof(cmd)));
+    free_order(ord);
+
+    ord = parse_order("STUDY AUTO Tactics", lang);
+    CuAssertIntEquals(tc, K_STUDY, getkeyword(ord));
+    CuAssertStrEquals(tc, "STUDY Tactics", get_command(ord, lang, cmd, sizeof(cmd)));
+    free_order(ord);
+
     test_teardown();
 }
 
diff --git a/src/skill.c b/src/skill.c
index 3d5b77a96..b279ab488 100644
--- a/src/skill.c
+++ b/src/skill.c
@@ -113,3 +113,38 @@ skill_t get_skill(const char *s, const struct locale * lang)
     return result;
 }
 
+int skill_cost(skill_t sk) {
+    static int config;
+    static int costs[MAXSKILLS];
+    int cost;
+    switch (sk) {
+    case SK_SPY:
+        cost = 100;
+        break;
+    case SK_TACTICS:
+    case SK_HERBALISM:
+    case SK_ALCHEMY:
+        cost = 200;
+        break;
+    default:
+        cost = -1;
+    }
+
+    if (config_changed(&config)) {
+        memset(costs, 0, sizeof(costs));
+    }
+
+    if (costs[sk] == 0) {
+        char buffer[256];
+        sprintf(buffer, "skills.cost.%s", skillnames[sk]);
+        costs[sk] = config_get_int(buffer, cost);
+    }
+    if (costs[sk] >= 0) {
+        return costs[sk];
+    }
+    return (cost > 0) ? cost : 0;
+}
+
+bool expensive_skill(skill_t sk) {
+    return (sk == SK_MAGIC) || skill_cost(sk) > 0;
+}
diff --git a/src/skill.h b/src/skill.h
index 3c88be725..ad6c7c2cb 100644
--- a/src/skill.h
+++ b/src/skill.h
@@ -49,5 +49,7 @@ void init_skills(const struct locale *lang);
 void init_skill(const struct locale *lang, skill_t kwd, const char *str);
 void enable_skill(skill_t sk, bool enabled);
 bool skill_enabled(skill_t sk);
+int skill_cost(skill_t sk);
+bool expensive_skill(skill_t sk);
 
 #endif
diff --git a/src/skill.test.c b/src/skill.test.c
index abfe529f9..09e9c8fe9 100644
--- a/src/skill.test.c
+++ b/src/skill.test.c
@@ -1,6 +1,8 @@
 #include <platform.h>
-#include "skill.h"
+
+#include "kernel/config.h"
 #include "util/language.h"
+#include "skill.h"
 #include "tests.h"
 
 #include <CuTest.h>
@@ -38,12 +40,34 @@ static void test_get_skill(CuTest *tc) {
     test_teardown();
 }
 
+static void test_skill_cost(CuTest *tc) {
+    test_setup();
+    CuAssertTrue(tc, expensive_skill(SK_MAGIC));
+    CuAssertTrue(tc, expensive_skill(SK_TACTICS));
+    CuAssertTrue(tc, expensive_skill(SK_SPY));
+    CuAssertTrue(tc, expensive_skill(SK_ALCHEMY));
+    CuAssertTrue(tc, expensive_skill(SK_HERBALISM));
+    CuAssertTrue(tc, !expensive_skill(SK_CROSSBOW));
+
+    CuAssertIntEquals(tc, 100, skill_cost(SK_SPY));
+    CuAssertIntEquals(tc, 200, skill_cost(SK_TACTICS));
+    CuAssertIntEquals(tc, 200, skill_cost(SK_ALCHEMY));
+    CuAssertIntEquals(tc, 200, skill_cost(SK_HERBALISM));
+    CuAssertIntEquals(tc, 0, skill_cost(SK_CROSSBOW));
+
+    config_set_int("skills.cost.crossbow", 300);
+    CuAssertIntEquals(tc, 300, skill_cost(SK_CROSSBOW));
+    CuAssertTrue(tc, expensive_skill(SK_CROSSBOW));
+    test_teardown();
+}
+
 CuSuite *get_skill_suite(void)
 {
     CuSuite *suite = CuSuiteNew();
     SUITE_ADD_TEST(suite, test_init_skill);
     SUITE_ADD_TEST(suite, test_init_skills);
     SUITE_ADD_TEST(suite, test_get_skill);
+    SUITE_ADD_TEST(suite, test_skill_cost);
     return suite;
 }
 
diff --git a/src/study.c b/src/study.c
index bd29fc2a8..c978e69ad 100644
--- a/src/study.c
+++ b/src/study.c
@@ -130,47 +130,23 @@ bool magic_lowskill(unit * u)
     return u_race(u) == toad_rc;
 }
 
-/* ------------------------------------------------------------- */
-
 int study_cost(struct unit *u, skill_t sk)
 {
-    static int config;
-    static int costs[MAXSKILLS];
-    int cost = -1;
-
     if (sk == SK_MAGIC) {
-        int next_level = 1 + (u ? get_level(u, sk) : 0);
+        static int config;
+        static int cost;
         /* Die Magiekosten betragen 50+Summe(50*Stufe) */
         /* 'Stufe' ist dabei die naechste zu erreichende Stufe */
-        cost = config_get_int("skills.cost.magic", 50);
-        return cost * (1 + ((next_level + next_level * next_level) / 2));
+        if (config_changed(&config)) {
+            cost = config_get_int("skills.cost.magic", 50);
+        }
+        if (cost > 0) {
+            int next_level = 1 + (u ? get_level(u, sk) : 0);
+            return cost * (1 + ((next_level + next_level * next_level) / 2));
+        }
+        return cost;
     }
-    else switch (sk) {
-    case SK_SPY:
-        cost = 100;
-        break;
-    case SK_TACTICS:
-    case SK_HERBALISM:
-    case SK_ALCHEMY:
-        cost = 200;
-        break;
-    default:
-        cost = -1;
-    }
-
-    if (config_changed(&config)) {
-        memset(costs, 0, sizeof(costs));
-    }
-
-    if (costs[sk] == 0) {
-        char buffer[256];
-        sprintf(buffer, "skills.cost.%s", skillnames[sk]);
-        costs[sk] = config_get_int(buffer, cost);
-    }
-    if (costs[sk] >= 0) {
-        return costs[sk];
-    }
-    return (cost > 0) ? cost : 0;
+    return skill_cost(sk);
 }
 
 /* ------------------------------------------------------------- */
diff --git a/src/study.h b/src/study.h
index e99fb4808..cca0428b7 100644
--- a/src/study.h
+++ b/src/study.h
@@ -29,26 +29,9 @@ extern "C" {
     struct unit;
     struct selist;
 
-    int teach_cmd(struct unit *u, struct order *ord);
-    int study_cmd(struct unit *u, struct order *ord);
-
-    magic_t getmagicskill(const struct locale *lang);
-    skill_t getskill(const struct locale *lang);
-    bool is_migrant(struct unit *u);
-    int study_cost(struct unit *u, skill_t talent);
-
-    typedef void(*learn_fun)(struct unit *u, skill_t sk, int days);
-
 #define STUDYDAYS 30
-    void learn_skill(struct unit *u, skill_t sk, int days);
-    void reduce_skill_days(struct unit *u, skill_t sk, int days);
-
-    void produceexp(struct unit *u, skill_t sk, int n);
-    void produceexp_ex(struct unit *u, skill_t sk, int n, learn_fun learn);
-
-    void demon_skillchange(struct unit *u);
-
 #define TEACHNUMBER 10
+
     typedef struct teaching_info {
         struct selist *teachers;
         int students;
@@ -57,6 +40,24 @@ extern "C" {
 
     extern const struct attrib_type at_learning;
 
+    int teach_cmd(struct unit *u, struct order *ord);
+    int study_cmd(struct unit *u, struct order *ord);
+
+    magic_t getmagicskill(const struct locale *lang);
+    skill_t getskill(const struct locale *lang);
+    bool is_migrant(struct unit *u);
+    int study_cost(struct unit *u, skill_t sk);
+
+    typedef void(*learn_fun)(struct unit *u, skill_t sk, int days);
+
+    void learn_skill(struct unit *u, skill_t sk, int days);
+    void reduce_skill_days(struct unit *u, skill_t sk, int days);
+
+    void produceexp(struct unit *u, skill_t sk, int n);
+    void produceexp_ex(struct unit *u, skill_t sk, int n, learn_fun learn);
+
+    void demon_skillchange(struct unit *u);
+
     void inject_learn(learn_fun fun);
 
 #ifdef __cplusplus

From 9456c4bdc7306f8be246764de07ef04aa04ce35f Mon Sep 17 00:00:00 2001
From: Enno Rehling <enno.rehling@gmail.com>
Date: Sun, 9 Sep 2018 15:16:11 +0200
Subject: [PATCH 6/6] fix debug logging.

---
 scripts/tests/e3/buildings.lua | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/scripts/tests/e3/buildings.lua b/scripts/tests/e3/buildings.lua
index e642c1aa7..9899d138e 100644
--- a/scripts/tests/e3/buildings.lua
+++ b/scripts/tests/e3/buildings.lua
@@ -42,7 +42,8 @@ function test_build_watch()
     process_orders()
     assert_not_nil(u.building)
     if 5 ~= u.building.size then
-        for k,v in f.messages do
+    -- debug logging to find intermittent errors
+        for k,v in ipairs(f.messages) do
             print(v)
         end
     end