diff --git a/clibs b/clibs
index 1854780fe..696505016 160000
--- a/clibs
+++ b/clibs
@@ -1 +1 @@
-Subproject commit 1854780fe3073e491775836c22f709668b1fff62
+Subproject commit 6965050165efdae89305a13bff06283229f143f4
diff --git a/iniparser b/iniparser
index 22741d9ce..e3533ac0a 160000
--- a/iniparser
+++ b/iniparser
@@ -1 +1 @@
-Subproject commit 22741d9ce9d19bf7b5f5a219b6ed0925259a4d1b
+Subproject commit e3533ac0a45e43e9716c40f6a874bc6f41ddc96d
diff --git a/process/compress.sh b/process/compress.sh
index 4c0147978..21ac828ef 100755
--- a/process/compress.sh
+++ b/process/compress.sh
@@ -2,7 +2,7 @@
if [ -z "$ERESSEA" ]; then
echo "You need to define the \$ERESSEA environment variable to run $0"
- exit -2
+ exit 2
fi
GAME="$ERESSEA/game-$1"
@@ -16,7 +16,7 @@ fi
if [ ! -d "$GAME/reports" ]; then
echo "cannot find reports directory in $GAME"
- exit -1
+ exit 1
fi
cd "$GAME/reports" || exit
diff --git a/process/cron/orders.cron b/process/cron/orders.cron
index 114a870a5..980f11ee2 100755
--- a/process/cron/orders.cron
+++ b/process/cron/orders.cron
@@ -11,10 +11,5 @@ fi
for GAME in $*
do
- if [ "$GAME" == "eressea" ]; then GAME=2 ; fi
- if [ "$GAME" == "e3a" ]; then GAME=3 ; fi
- if [ -e $HOME/eressea/game-$GAME/orders.queue ]
- then
- $HOME/bin/orders-process $GAME
- fi
+ $HOME/eressea/orders-php/check-orders.sh $GAME
done
diff --git a/process/epasswd.py b/process/epasswd.py
index c5e1712b9..0f38fd593 100755
--- a/process/epasswd.py
+++ b/process/epasswd.py
@@ -3,34 +3,9 @@
from string import split
from string import strip
from string import lower
-import subprocess
import bcrypt
import sqlite3
-def baseconvert(n, base):
- """convert positive decimal integer n to equivalent in another base (2-36)"""
-
- digits = "0123456789abcdefghijkLmnopqrstuvwxyz"
-
- try:
- n = int(n)
- base = int(base)
- except:
- return ""
-
- if n < 0 or base < 2 or base > 36:
- return ""
-
- s = ""
- while True:
- r = n % base
- s = digits[r] + s
- n = n / base
- if n == 0:
- break
-
- return s
-
class EPasswd:
def __init__(self):
self.data = {}
@@ -47,7 +22,7 @@ class EPasswd:
c = conn.cursor()
for row in c.execute('SELECT `no`, `email`, `password` FROM `faction`'):
(no, email, passwd) = row
- self.set_data(baseconvert(no, 36), email, passwd)
+ self.set_data(no, email, passwd)
conn.close()
def load_file(self, file):
diff --git a/process/orders-accept b/process/orders-accept
index 99398e64f..dfadb3aea 100755
--- a/process/orders-accept
+++ b/process/orders-accept
@@ -2,6 +2,7 @@
# example: orders-accept 2 de < mail.txt
game="$1"
+lang="$2"
[ -z "$ERESSEA" ] && ERESSEA="$HOME/eressea"
SCRIPT=$(readlink -f "$0")
@@ -21,5 +22,5 @@ filename=$(basename "$ACCEPT_FILE")
email="$ACCEPT_MAIL"
if [ -d "$ERESSEA/orders-php" ]
then
- php "$ERESSEA/orders-php/cli.php" insert "$filename" "$email"
+ php "$ERESSEA/orders-php/cli.php" insert "$filename" "$lang" "$email"
fi
diff --git a/process/send-bz2-report b/process/send-bz2-report
index 51a8113bf..3eabeca30 100755
--- a/process/send-bz2-report
+++ b/process/send-bz2-report
@@ -5,7 +5,7 @@ if [ -z "$ERESSEA" ]; then
fi
if [ ! -f reports.txt ]; then
echo "need to run $0 from the report direcory"
- exit -2
+ exit 2
fi
TEMPLATE=report-mail.txt
diff --git a/process/send-zip-report b/process/send-zip-report
index a6bd85246..1e3da1933 100755
--- a/process/send-zip-report
+++ b/process/send-zip-report
@@ -6,7 +6,7 @@ if [ -z "$ERESSEA" ]; then
fi
if [ ! -f reports.txt ]; then
echo "need to run $0 from the report direcory"
- exit -2
+ exit 2
fi
PWD=$(pwd)
@@ -33,7 +33,7 @@ fi
if [ ! -e "$TEMPLATE" ]; then
echo "no such email template: $TEMPLATE"
- exit -3
+ exit 3
fi
while [ -e /tmp/.stopped ] ; do
diff --git a/process/sendreport.sh b/process/sendreport.sh
index d38b2a14f..c19c664a7 100755
--- a/process/sendreport.sh
+++ b/process/sendreport.sh
@@ -3,14 +3,14 @@
if [ -z "$ERESSEA" ]; then
echo "You have to define the \$ERESSEA environment variable to run $0"
- exit -2
+ exit 2
fi
function abort() {
if [ $# -gt 0 ]; then
echo "$@"
fi
- exit -1
+ exit 1
}
GAME=$1
@@ -47,7 +47,7 @@ fi
bash "${FACTION}.sh" "$EMAIL" || reply "Unbekannte Partei $FACTION"
OWNER=$("$BIN/getfaction.py" "$PWFILE" "$FACTION")
-if [ ! -z "$OWNER" ]; then
+if [ -n "$OWNER" ]; then
echo "Der Report Deiner Partei wurde an ${EMAIL} gesandt." \
| mutt -s "Reportnachforderung Partei ${FACTION}" "$OWNER"
fi
diff --git a/process/sendreports.sh b/process/sendreports.sh
index d1adba943..c56233367 100755
--- a/process/sendreports.sh
+++ b/process/sendreports.sh
@@ -5,10 +5,10 @@
if [ -z "$ERESSEA" ]; then
echo "You have to define the \$ERESSEA environment variable to run $0"
- exit -2
+ exit 2
fi
-if [ ! -z "$1" ]; then
+if [ -n "$1" ]; then
GAME="$ERESSEA/game-$1"
else
GAME=$ERESSEA
diff --git a/res/core/messages.xml b/res/core/messages.xml
index 4de8b0299..766cd481c 100644
--- a/res/core/messages.xml
+++ b/res/core/messages.xml
@@ -4391,6 +4391,13 @@
+
+
+
+
+
+
+
@@ -5196,6 +5203,12 @@
+
+
+
+
+
+
diff --git a/res/core/ships.xml b/res/core/ships.xml
index d26975285..48e77dec6 100644
--- a/res/core/ships.xml
+++ b/res/core/ships.xml
@@ -8,6 +8,13 @@
+
+
+
+
+
+
+
diff --git a/res/translations/messages.de.po b/res/translations/messages.de.po
index 5623069d4..a1d30c85c 100644
--- a/res/translations/messages.de.po
+++ b/res/translations/messages.de.po
@@ -38,6 +38,9 @@ msgstr "\"$unit($mage) verwandelt $unit($target) in $race($race,0).\""
msgid "give_person"
msgstr "\"$unit($unit) übergibt $int($amount) Person$if($eq($amount,1),\"\",\"en\") an $unit($target).\""
+msgid "transfer_unit"
+msgstr "\"$unit($unit) schließt sich unserer Partei an.\""
+
msgid "rust_effect_2"
msgstr "\"$unit($mage) ruft ein fürchterliches Unwetter über seine Feinde. Der magischen Regen lässt alles Eisen rosten.\""
@@ -686,6 +689,9 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - Die Einheit kan
msgid "error85"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Es wurde keine Emailadresse angegeben.\""
+msgid "error125"
+msgstr "\"$unit($unit) in $region($region): '$order($command)' - Es wurde kein Banner angegeben.\""
+
msgid "starvation"
msgstr "\"$unit($unit) verliert in $region($region) $int($dead) von $int($add($live,$dead)) Personen durch Unterernährung.\""
diff --git a/res/translations/messages.en.po b/res/translations/messages.en.po
index 1f1db551c..037b50d4d 100644
--- a/res/translations/messages.en.po
+++ b/res/translations/messages.en.po
@@ -38,6 +38,9 @@ msgstr "\"$unit($mage) tranforms $unit($target) to $race($race,0).\""
msgid "give_person"
msgstr "\"$unit($unit) transfers $int($amount) person$if($eq($amount,1),\"\",\"s\") to $unit($target).\""
+msgid "transfer_unit"
+msgstr "\"$unit($unit) joins our faction.\""
+
msgid "rust_effect_2"
msgstr "\"$unit($mage) calls forth a terrible torment over the enemy. The magical rain makes all iron rusty.\""
@@ -686,6 +689,9 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - The unit cannot
msgid "error85"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - No email address was supplied.\""
+msgid "error125"
+msgstr "\"$unit($unit) in $region($region): '$order($command)' - No banner text was supplied.\""
+
msgid "starvation"
msgstr "\"$unit($unit) loses $int($dead) of $int($add($live,$dead)) people due to starvation in $region($region).\""
diff --git a/res/translations/strings.de.po b/res/translations/strings.de.po
index 8720fc85d..5884eda83 100644
--- a/res/translations/strings.de.po
+++ b/res/translations/strings.de.po
@@ -1707,6 +1707,9 @@ msgstr "Ring der Unsichtbarkeit"
msgid "caravel_a"
msgstr "eine Karavelle"
+msgid "galleon_a"
+msgstr "eine Galleone"
+
msgctxt "keyword"
msgid "describe"
msgstr "BESCHREIBE"
@@ -2064,6 +2067,9 @@ msgstr "Wir schreiben %s des Monats %s im Jahre %d %s."
msgid "caravel"
msgstr "Karavelle"
+msgid "galleon"
+msgstr "Galleone"
+
msgid "dragon_postfix_10"
msgstr "der Goldene"
diff --git a/res/translations/strings.en.po b/res/translations/strings.en.po
index d096a77ae..13badb9cb 100644
--- a/res/translations/strings.en.po
+++ b/res/translations/strings.en.po
@@ -1458,6 +1458,12 @@ msgstr "DESCRIBE"
msgid "roi"
msgstr "ring of invisibility"
+msgid "galleon_a"
+msgstr "a galleon"
+
+msgid "galleon"
+msgstr "galleon"
+
msgid "caravel_a"
msgstr "a caravel"
diff --git a/s/convert.sh b/s/convert.sh
new file mode 100644
index 000000000..6a18ad8a0
--- /dev/null
+++ b/s/convert.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+while [ ! -z $1 ] ; do
+ tmpfile=$(mktemp eressea.XXX)
+ iconv -f latin1 -t utf-8 < $1 | \
+ perl -pe 's/ß/ss/; s/ä/ae/; s/ü/ue/; s/ö/oe/;' \
+ > $tmpfile && \mv $tmpfile $1
+ file $1
+ shift 1
+done
diff --git a/scripts/eressea/xmasitems.lua b/scripts/eressea/xmasitems.lua
index 3771553b5..2d8f397a1 100644
--- a/scripts/eressea/xmasitems.lua
+++ b/scripts/eressea/xmasitems.lua
@@ -37,6 +37,7 @@ function use_snowglobe(u, amount, token, ord)
local transform = {
ocean = "glacier",
firewall = "volcano",
+ activevolcano = "volcano",
volcano = "mountain",
desert = "plain"
}
diff --git a/scripts/run-turn.lua b/scripts/run-turn.lua
index 0540d2321..e5c90a7be 100644
--- a/scripts/run-turn.lua
+++ b/scripts/run-turn.lua
@@ -95,11 +95,11 @@ local function write_htpasswd()
end
local function write_files(locales)
+ write_reports()
+ write_summary()
write_database()
write_passwords()
write_htpasswd()
- write_reports()
- write_summary()
end
local function write_scores()
diff --git a/scripts/tests/e2/init.lua b/scripts/tests/e2/init.lua
index fde840f98..420ba3e06 100644
--- a/scripts/tests/e2/init.lua
+++ b/scripts/tests/e2/init.lua
@@ -1,3 +1,4 @@
+require 'tests.e2.quit'
require 'tests.e2.movement'
require 'tests.e2.astral'
require 'tests.e2.spells'
diff --git a/scripts/tests/e2/quit.lua b/scripts/tests/e2/quit.lua
new file mode 100644
index 000000000..7db59091b
--- /dev/null
+++ b/scripts/tests/e2/quit.lua
@@ -0,0 +1,21 @@
+require "lunit"
+
+module("tests.e2.quit", package.seeall, lunit.testcase)
+
+function test_quit_faction()
+ local r = region.create(47, 0, "plain")
+ local f1 = faction.create("human")
+ f1.password = "steamedhams"
+ local f2 = faction.create("human")
+ local u1 = unit.create(f1, r, 8)
+ local u2 = unit.create(f2, r, 9)
+ local u3 = unit.create(f1, r, 10)
+ u1:clear_orders()
+ u2:clear_orders()
+ u1:add_order("STIRB steamedhams PARTEI " .. itoa36(f2.id))
+ u2:add_order("KONTAKTIERE " .. itoa36(u1.id))
+ process_orders()
+ assert_equal(f2, u1.faction)
+ assert_equal(f2, u2.faction)
+ assert_equal(f2, u3.faction)
+end
diff --git a/scripts/tests/init.lua b/scripts/tests/init.lua
index a01e1c75c..2c0786347 100644
--- a/scripts/tests/init.lua
+++ b/scripts/tests/init.lua
@@ -1,4 +1,5 @@
-- new tests 2014-06-11
+require 'tests.laws'
require 'tests.faction'
require 'tests.locale'
require 'tests.movement'
@@ -6,6 +7,5 @@ require 'tests.pool'
require 'tests.regions'
require 'tests.settings'
require 'tests.study'
-require 'tests.laws'
require 'tests.bindings'
require 'tests.hunger'
diff --git a/scripts/tests/orders.lua b/scripts/tests/orders.lua
index 63abaed00..6dc191d6c 100644
--- a/scripts/tests/orders.lua
+++ b/scripts/tests/orders.lua
@@ -70,15 +70,15 @@ end
function test_give_temp()
u.number = 2
u:add_order("GIB TEMP 123 1 PERSON")
- u:add_order("MACHE TEMP 123 'Herpderp'")
+ u:add_order("MACHE TEMP 123 'Lorax'")
u:add_order("ENDE")
_G.process_orders()
assert_equal(1, u.number)
for x in f.units do
- if x.name == 'Herpderp' then u=x end
+ if x.name == 'Lorax' then u=x end
end
- assert_equal('Herpderp', u.name)
+ assert_equal('Lorax', u.name)
assert_equal(1, u.number)
end
diff --git a/scripts/tests/report.lua b/scripts/tests/report.lua
index b65bcc279..b02f4a908 100644
--- a/scripts/tests/report.lua
+++ b/scripts/tests/report.lua
@@ -94,7 +94,9 @@ function test_lighthouse()
eressea.free_game()
local r = region.create(0, 0, "mountain")
local f = faction.create("human", "human@example.com")
- region.create(1, 0, "mountain")
+ local f2 = faction.create("dwarf")
+ local r2 = region.create(1, 0, "mountain")
+ unit.create(f2, r2, 1).name = 'The Babadook'
region.create(2, 0, "ocean")
region.create(0, 1, "firewall")
region.create(3, 0, "ocean")
@@ -110,12 +112,13 @@ function test_lighthouse()
init_reports()
write_report(f)
- assert_false(find_in_report(f, " %(1,0%) %(vom Turm erblickt%)"))
+ assert_false(find_in_report(f, "The Babadook"))
+ assert_true(find_in_report(f, " %(1,0%) %(vom Turm erblickt%)"))
assert_true(find_in_report(f, " %(2,0%) %(vom Turm erblickt%)"))
assert_true(find_in_report(f, " %(3,0%) %(vom Turm erblickt%)"))
+ assert_true(find_in_report(f, " %(0,1%) %(vom Turm erblickt%)"))
assert_false(find_in_report(f, " %(0,0%) %(vom Turm erblickt%)"))
- assert_false(find_in_report(f, " %(0,1%) %(vom Turm erblickt%)"))
assert_false(find_in_report(f, " %(4,0%) %(vom Turm erblickt%)"))
remove_report(f)
end
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index bd0ed2bff..76764f0bd 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -101,6 +101,7 @@ set (ERESSEA_SRC
creport.c
direction.c
donations.c
+ recruit.c
economy.c
eressea.c
exparse.c
diff --git a/src/academy.c b/src/academy.c
index 01ff9fd1c..c8a7e5a14 100644
--- a/src/academy.c
+++ b/src/academy.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/alchemy.c b/src/alchemy.c
index b4e7f6253..3d3fe9836 100644
--- a/src/alchemy.c
+++ b/src/alchemy.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/attributes/attributes.c b/src/attributes/attributes.c
index d0be86b0e..2c82603fb 100644
--- a/src/attributes/attributes.c
+++ b/src/attributes/attributes.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/attributes/dict.c b/src/attributes/dict.c
index 130dfffa4..caa7d7e6a 100644
--- a/src/attributes/dict.c
+++ b/src/attributes/dict.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/attributes/follow.c b/src/attributes/follow.c
index caeecee54..c81e5630d 100644
--- a/src/attributes/follow.c
+++ b/src/attributes/follow.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/attributes/hate.c b/src/attributes/hate.c
index cef483cf6..678ee9922 100644
--- a/src/attributes/hate.c
+++ b/src/attributes/hate.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/attributes/iceberg.c b/src/attributes/iceberg.c
index 192ae9032..0c6e4e288 100644
--- a/src/attributes/iceberg.c
+++ b/src/attributes/iceberg.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/attributes/key.c b/src/attributes/key.c
index 8d563024d..10f7c49a9 100644
--- a/src/attributes/key.c
+++ b/src/attributes/key.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/attributes/movement.c b/src/attributes/movement.c
index 01c75ffae..19c5fb774 100644
--- a/src/attributes/movement.c
+++ b/src/attributes/movement.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/attributes/otherfaction.c b/src/attributes/otherfaction.c
index fcf05da05..5f4c83086 100644
--- a/src/attributes/otherfaction.c
+++ b/src/attributes/otherfaction.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/attributes/overrideroads.c b/src/attributes/overrideroads.c
index d96d115d9..8fbd18972 100644
--- a/src/attributes/overrideroads.c
+++ b/src/attributes/overrideroads.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/attributes/racename.c b/src/attributes/racename.c
index 9374f1238..880f32d57 100644
--- a/src/attributes/racename.c
+++ b/src/attributes/racename.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/attributes/raceprefix.c b/src/attributes/raceprefix.c
index 84997b909..68a8923d4 100644
--- a/src/attributes/raceprefix.c
+++ b/src/attributes/raceprefix.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/attributes/reduceproduction.c b/src/attributes/reduceproduction.c
index 0ae0abbea..57bfa4b93 100644
--- a/src/attributes/reduceproduction.c
+++ b/src/attributes/reduceproduction.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/attributes/seenspell.c b/src/attributes/seenspell.c
index 698a83076..442660860 100644
--- a/src/attributes/seenspell.c
+++ b/src/attributes/seenspell.c
@@ -1,6 +1,5 @@
/*
-Copyright (c) 1998-2018,
-Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/attributes/targetregion.c b/src/attributes/targetregion.c
index 4f5a2101a..2c29f8216 100644
--- a/src/attributes/targetregion.c
+++ b/src/attributes/targetregion.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/automate.c b/src/automate.c
index 7fd0c5d62..2c9a94ad0 100644
--- a/src/automate.c
+++ b/src/automate.c
@@ -1,5 +1,6 @@
#include
+#include "kernel/config.h"
#include "kernel/faction.h"
#include "kernel/messages.h"
#include "kernel/order.h"
@@ -16,48 +17,57 @@
#include
#include
-static int cmp_scholars(const void *lhs, const void *rhs)
-{
+static int cmp_scholars(const void *lhs, const void *rhs) {
const scholar *a = (const scholar *)lhs;
const scholar *b = (const scholar *)rhs;
- if (a->sk == b->sk) {
- /* sort by level, descending: */
- return b->level - a->level;
- }
- /* order by skill */
- return (int)a->sk - (int)b->sk;
+ return b->level - a->level;
}
-int autostudy_init(scholar scholars[], int max_scholars, unit **units)
+int autostudy_init(scholar scholars[], int max_scholars, unit **units, skill_t *o_skill)
{
unit *unext = NULL, *u = *units;
faction *f = u->faction;
int nscholars = 0;
-
+ skill_t skill = NOSKILL;
while (u) {
- keyword_t kwd = init_order(u->thisorder, u->faction->locale);
- if (kwd == K_AUTOSTUDY) {
- if (long_order_allowed(u)) {
+ if (!fval(u, UFL_MARK)) {
+ keyword_t kwd = init_order(u->thisorder, u->faction->locale);
+ if (kwd == K_AUTOSTUDY) {
if (f == u->faction) {
- scholar * st = scholars + nscholars;
- skill_t sk = getskill(u->faction->locale);
- if (check_student(u, u->thisorder, sk)) {
- st->sk = sk;
- st->level = effskill_study(u, st->sk);
- st->learn = 0;
- st->u = u;
- if (++nscholars > max_scholars) {
- log_fatal("you must increase MAXSCHOLARS");
+ unext = u->next;
+ if (long_order_allowed(u)) {
+ scholar * st = scholars + nscholars;
+ skill_t sk = getskill(u->faction->locale);
+ if (skill == NOSKILL && sk != NOSKILL) {
+ skill = sk;
+ if (o_skill) {
+ *o_skill = skill;
+ }
+ }
+ if (check_student(u, u->thisorder, sk)) {
+ if (sk == skill) {
+ fset(u, UFL_MARK);
+ st->level = (short)effskill_study(u, sk);
+ st->learn = 0;
+ st->u = u;
+ if (++nscholars >= max_scholars) {
+ log_warning("you must increase MAXSCHOLARS");
+ break;
+ }
+ }
+ }
+ else {
+ fset(u, UFL_MARK);
}
}
}
- else if (!unext) {
- unext = u;
- }
}
}
u = u->next;
}
+ while (unext && unext->faction != f) {
+ unext = unext->next;
+ }
*units = unext;
if (nscholars > 0) {
qsort(scholars, nscholars, sizeof(scholar), cmp_scholars);
@@ -81,26 +91,25 @@ void autostudy_run(scholar scholars[], int nscholars)
{
int ti = 0;
while (ti != nscholars) {
- skill_t sk = scholars[ti].sk;
int t, se, ts = 0, tt = 0, si = ti;
- for (se = ti; se != nscholars && scholars[se].sk == sk; ++se) {
+ for (se = ti; se != nscholars; ++se) {
int mint;
ts += scholars[se].u->number; /* count total scholars */
mint = (ts + 10) / 11; /* need a minimum of ceil(ts/11) teachers */
- for (; mint > tt && si != nscholars && scholars[si].sk == sk; ++si) {
+ for (; mint > tt && si != nscholars; ++si) {
tt += scholars[si].u->number;
}
}
/* now si splits the teachers and students 1:10 */
/* first student must be 2 levels below first teacher: */
- for (; si != se && scholars[si].sk == sk; ++si) {
+ for (; si != se; ++si) {
if (scholars[si].level + TEACHDIFFERENCE <= scholars[ti].level) {
break;
}
tt += scholars[si].u->number;
}
/* now si is the first unit we can teach, if we can teach any */
- if (si == se || scholars[si].sk != sk) {
+ if (si == se) {
/* there are no students, so standard learning for everyone */
for (t = ti; t != se; ++t) {
learning(scholars + t, scholars[t].u->number);
@@ -161,23 +170,38 @@ void autostudy_run(scholar scholars[], int nscholars)
}
}
-#define MAXSCHOLARS 512
-
void do_autostudy(region *r)
{
+ static int config;
+ static int batchsize = MAXSCHOLARS;
static int max_scholars;
- unit *units = r->units;
scholar scholars[MAXSCHOLARS];
- while (units) {
- int i, nscholars = autostudy_init(scholars, MAXSCHOLARS, &units);
- if (nscholars > max_scholars) {
- stats_count("automate.max_scholars", nscholars - max_scholars);
- max_scholars = nscholars;
- }
- autostudy_run(scholars, nscholars);
- for (i = 0; i != nscholars; ++i) {
- int days = STUDYDAYS * scholars[i].learn;
- learn_skill(scholars[i].u, scholars[i].sk, days);
+ unit *u;
+
+ if (config_changed(&config)) {
+ batchsize = config_get_int("automate.batchsize", MAXSCHOLARS);
+ assert(batchsize <= MAXSCHOLARS);
+ }
+ for (u = r->units; u; u = u->next) {
+ if (!fval(u, UFL_MARK)) {
+ unit *ulist = u;
+ int sum_scholars = 0;
+ while (ulist) {
+ skill_t skill = NOSKILL;
+ int i, nscholars = autostudy_init(scholars, batchsize, &ulist, &skill);
+ assert(ulist == NULL || ulist->faction == u->faction);
+ sum_scholars += nscholars;
+ if (sum_scholars > max_scholars) {
+ stats_count("automate.max_scholars", sum_scholars - max_scholars);
+ max_scholars = sum_scholars;
+ }
+ autostudy_run(scholars, nscholars);
+ for (i = 0; i != nscholars; ++i) {
+ int days = STUDYDAYS * scholars[i].learn;
+ learn_skill(scholars[i].u, skill, days);
+ }
+ }
}
+ freset(u, UFL_MARK);
}
}
diff --git a/src/automate.h b/src/automate.h
index 314791b8f..7c71259b2 100644
--- a/src/automate.h
+++ b/src/automate.h
@@ -28,16 +28,16 @@ struct unit;
typedef struct scholar {
struct unit *u;
- skill_t sk;
- int level;
int learn;
+ short level;
} scholar;
+#define MAXSCHOLARS 128
#define STUDENTS_PER_TEACHER 10
void do_autostudy(struct region *r);
-int autostudy_init(scholar scholars[], int max_scholars, struct unit **units);
+int autostudy_init(scholar scholars[], int max_scholars, struct unit **units, skill_t *o_skill);
void autostudy_run(scholar scholars[], int nscholars);
#endif
diff --git a/src/automate.test.c b/src/automate.test.c
index 698cda6be..5508cce88 100644
--- a/src/automate.test.c
+++ b/src/automate.test.c
@@ -4,6 +4,7 @@
#include "automate.h"
+#include "kernel/config.h"
#include "kernel/faction.h"
#include "kernel/order.h"
#include "kernel/region.h"
@@ -20,6 +21,8 @@ static void test_autostudy_init(CuTest *tc) {
unit *u1, *u2, *u3, *u4, *u5, *ulist;
faction *f;
region *r;
+ message *msg;
+ skill_t skill = NOSKILL;
test_setup();
mt_create_error(77);
@@ -33,36 +36,36 @@ static void test_autostudy_init(CuTest *tc) {
u2 = test_create_unit(f, r);
u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]);
set_level(u2, SK_ENTERTAINMENT, 2);
- u3 = test_create_unit(f, r);
- u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
u4 = test_create_unit(f, r);
u4->thisorder = create_order(K_AUTOSTUDY, f->locale, "Dudelidu");
+ u3 = test_create_unit(f, r);
+ u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
u5 = test_create_unit(test_create_faction(NULL), r);
u5->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
- scholars[3].u = NULL;
+ scholars[2].u = NULL;
+
ulist = r->units;
- CuAssertIntEquals(tc, 3, autostudy_init(scholars, 4, &ulist));
- CuAssertPtrNotNull(tc, test_find_messagetype(u4->faction->msgs, "error77"));
+ CuAssertIntEquals(tc, 2, autostudy_init(scholars, 4, &ulist, &skill));
+ CuAssertIntEquals(tc, SK_ENTERTAINMENT, skill);
CuAssertPtrEquals(tc, u2, scholars[0].u);
CuAssertIntEquals(tc, 2, scholars[0].level);
CuAssertIntEquals(tc, 0, scholars[0].learn);
- CuAssertIntEquals(tc, SK_ENTERTAINMENT, scholars[0].sk);
CuAssertPtrEquals(tc, u1, scholars[1].u);
CuAssertIntEquals(tc, 0, scholars[1].level);
CuAssertIntEquals(tc, 0, scholars[1].learn);
- CuAssertIntEquals(tc, SK_ENTERTAINMENT, scholars[1].sk);
- CuAssertPtrEquals(tc, u3, scholars[2].u);
- CuAssertIntEquals(tc, 0, scholars[2].level);
- CuAssertIntEquals(tc, 0, scholars[2].learn);
- CuAssertIntEquals(tc, SK_PERCEPTION, scholars[2].sk);
- CuAssertPtrEquals(tc, NULL, scholars[3].u);
- CuAssertPtrEquals(tc, u5, ulist);
- CuAssertIntEquals(tc, 1, autostudy_init(scholars, 4, &ulist));
- CuAssertPtrEquals(tc, u5, scholars[0].u);
+ CuAssertPtrEquals(tc, NULL, scholars[2].u);
+ CuAssertPtrEquals(tc, NULL, ulist);
+
+ ulist = u3;
+ CuAssertIntEquals(tc, 1, autostudy_init(scholars, 4, &ulist, &skill));
+ CuAssertIntEquals(tc, SK_PERCEPTION, skill);
+ CuAssertPtrEquals(tc, u3, scholars[0].u);
CuAssertIntEquals(tc, 0, scholars[0].level);
CuAssertIntEquals(tc, 0, scholars[0].learn);
- CuAssertIntEquals(tc, SK_PERCEPTION, scholars[0].sk);
CuAssertPtrEquals(tc, NULL, ulist);
+
+ CuAssertPtrNotNull(tc, msg = test_find_messagetype(f->msgs, "error77"));
+ CuAssertPtrEquals(tc, NULL, test_find_messagetype_ex(f->msgs, "error77", msg));
test_teardown();
}
@@ -75,6 +78,7 @@ static void test_autostudy_run_twoteachers(CuTest *tc) {
unit *u1, *u2, *u3, *u4, *ulist;
faction *f;
region *r;
+ skill_t skill;
test_setup();
r = test_create_plain(0, 0);
@@ -94,9 +98,10 @@ static void test_autostudy_run_twoteachers(CuTest *tc) {
set_number(u4, 12);
ulist = r->units;
- CuAssertIntEquals(tc, 4, nscholars = autostudy_init(scholars, 4, &ulist));
+ CuAssertIntEquals(tc, 4, nscholars = autostudy_init(scholars, 4, &ulist, &skill));
CuAssertPtrEquals(tc, NULL, ulist);
autostudy_run(scholars, nscholars);
+ CuAssertIntEquals(tc, SK_ENTERTAINMENT, skill);
CuAssertIntEquals(tc, 0, scholars[0].learn);
CuAssertIntEquals(tc, 0, scholars[1].learn);
CuAssertIntEquals(tc, scholars[2].u->number * 2, scholars[2].learn);
@@ -125,21 +130,34 @@ static void test_autostudy_run(CuTest *tc) {
u3 = test_create_unit(f, r);
u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
set_number(u3, 15);
- scholars[3].u = NULL;
+
+ scholars[2].u = NULL;
ulist = r->units;
- CuAssertIntEquals(tc, 3, nscholars = autostudy_init(scholars, 4, &ulist));
+ CuAssertIntEquals(tc, 2, nscholars = autostudy_init(scholars, 4, &ulist, NULL));
+ CuAssertIntEquals(tc, UFL_MARK, u1->flags & UFL_MARK);
+ CuAssertIntEquals(tc, UFL_MARK, u2->flags & UFL_MARK);
+ CuAssertIntEquals(tc, 0, u3->flags & UFL_MARK);
CuAssertPtrEquals(tc, NULL, ulist);
autostudy_run(scholars, nscholars);
CuAssertIntEquals(tc, 1, scholars[0].learn);
CuAssertIntEquals(tc, 20, scholars[1].learn);
- CuAssertIntEquals(tc, 15, scholars[2].learn);
+ CuAssertPtrEquals(tc, NULL, scholars[2].u);
+
+ scholars[1].u = NULL;
+ ulist = u3;
+ CuAssertIntEquals(tc, 1, nscholars = autostudy_init(scholars, 4, &ulist, NULL));
+ CuAssertPtrEquals(tc, NULL, ulist);
+ autostudy_run(scholars, nscholars);
+ CuAssertIntEquals(tc, 15, scholars[0].learn);
+ CuAssertPtrEquals(tc, NULL, scholars[1].u);
+
test_teardown();
}
static void test_autostudy_run_noteachers(CuTest *tc) {
scholar scholars[4];
int nscholars;
- unit *u1, *u2, *u3, *ulist;
+ unit *u1, *u2, *ulist;
faction *f;
region *r;
@@ -147,23 +165,24 @@ static void test_autostudy_run_noteachers(CuTest *tc) {
r = test_create_plain(0, 0);
f = test_create_faction(NULL);
u1 = test_create_unit(f, r);
- u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_LUMBERJACK]);
- set_number(u1, 2);
+ u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]);
+ set_number(u1, 5);
set_level(u1, SK_ENTERTAINMENT, 2);
+
u2 = test_create_unit(f, r);
u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]);
- set_number(u2, 10);
- u3 = test_create_unit(f, r);
- u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
- set_number(u3, 15);
- scholars[3].u = NULL;
+ set_number(u2, 7);
+ set_level(u2, SK_ENTERTAINMENT, 2);
+
+ scholars[2].u = NULL;
ulist = r->units;
- CuAssertIntEquals(tc, 3, nscholars = autostudy_init(scholars, 4, &ulist));
+ CuAssertIntEquals(tc, 2, nscholars = autostudy_init(scholars, 4, &ulist, NULL));
CuAssertPtrEquals(tc, NULL, ulist);
autostudy_run(scholars, nscholars);
- CuAssertIntEquals(tc, 2, scholars[0].learn);
- CuAssertIntEquals(tc, 10, scholars[1].learn);
- CuAssertIntEquals(tc, 15, scholars[2].learn);
+ /* stupid qsort is unstable: */
+ CuAssertIntEquals(tc, 12, scholars[0].learn + scholars[1].learn);
+ CuAssertIntEquals(tc, 35, scholars[0].learn * scholars[1].learn);
+ CuAssertPtrEquals(tc, NULL, scholars[2].u);
test_teardown();
}
@@ -188,7 +207,7 @@ static void test_autostudy_run_teachers_learn(CuTest *tc) {
u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]);
set_number(u2, 10);
ulist = r->units;
- CuAssertIntEquals(tc, 2, nscholars = autostudy_init(scholars, 4, &ulist));
+ CuAssertIntEquals(tc, 2, nscholars = autostudy_init(scholars, 4, &ulist, NULL));
CuAssertPtrEquals(tc, NULL, ulist);
autostudy_run(scholars, nscholars);
CuAssertIntEquals(tc, 1, scholars[0].learn);
@@ -222,7 +241,7 @@ static void test_autostudy_run_skilldiff(CuTest *tc) {
set_number(u3, 10);
scholars[3].u = NULL;
ulist = r->units;
- CuAssertIntEquals(tc, 3, nscholars = autostudy_init(scholars, 4, &ulist));
+ CuAssertIntEquals(tc, 3, nscholars = autostudy_init(scholars, 4, &ulist, NULL));
CuAssertPtrEquals(tc, NULL, ulist);
autostudy_run(scholars, nscholars);
CuAssertIntEquals(tc, 0, scholars[0].learn);
@@ -231,11 +250,78 @@ static void test_autostudy_run_skilldiff(CuTest *tc) {
test_teardown();
}
+static void test_autostudy_batches(CuTest *tc) {
+ scholar scholars[2];
+ int nscholars;
+ unit *u1, *u2, *u3, *ulist;
+ faction *f;
+ region *r;
+
+ test_setup();
+ r = test_create_plain(0, 0);
+ f = test_create_faction(NULL);
+ u1 = test_create_unit(f, r);
+ u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
+ set_number(u1, 1);
+ set_level(u1, SK_PERCEPTION, 2);
+ u2 = test_create_unit(f, r);
+ u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
+ set_number(u2, 10);
+ u3 = test_create_unit(f, r);
+ u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
+ set_number(u3, 10);
+ scholars[1].u = NULL;
+ ulist = r->units;
+ config_set("automate.batchsize", "2");
+ CuAssertIntEquals(tc, 2, nscholars = autostudy_init(scholars, 2, &ulist, NULL));
+ CuAssertPtrEquals(tc, u3, ulist);
+ autostudy_run(scholars, nscholars);
+ CuAssertIntEquals(tc, 0, scholars[0].learn);
+ CuAssertIntEquals(tc, 20, scholars[1].learn);
+ CuAssertIntEquals(tc, 1, nscholars = autostudy_init(scholars, 2, &ulist, NULL));
+ autostudy_run(scholars, nscholars);
+ CuAssertIntEquals(tc, 10, scholars[0].learn);
+ test_teardown();
+}
+
+static void test_do_autostudy(CuTest *tc) {
+ unit *u1, *u2, *u3, *u4;
+ faction *f;
+ region *r;
+
+ test_setup();
+ r = test_create_plain(0, 0);
+ f = test_create_faction(NULL);
+ u1 = test_create_unit(f, r);
+ u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
+ set_number(u1, 1);
+ set_level(u1, SK_PERCEPTION, 2);
+ u2 = test_create_unit(f, r);
+ u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
+ set_number(u2, 10);
+ u3 = test_create_unit(f, r);
+ u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]);
+ u4 = test_create_unit(test_create_faction(NULL), r);
+ u4->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]);
+ do_autostudy(r);
+ CuAssertIntEquals(tc, 2, get_level(u1, SK_PERCEPTION));
+ /* impossible to say if u2 is T1 or T2 now */
+ CuAssertIntEquals(tc, 1, get_level(u3, SK_ENTERTAINMENT));
+ CuAssertIntEquals(tc, 1, get_level(u4, SK_ENTERTAINMENT));
+ CuAssertIntEquals(tc, 0, u1->flags & UFL_MARK);
+ CuAssertIntEquals(tc, 0, u2->flags & UFL_MARK);
+ CuAssertIntEquals(tc, 0, u3->flags & UFL_MARK);
+ CuAssertIntEquals(tc, 0, u4->flags & UFL_MARK);
+ test_teardown();
+}
+
CuSuite *get_automate_suite(void)
{
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_autostudy_init);
SUITE_ADD_TEST(suite, test_autostudy_run);
+ SUITE_ADD_TEST(suite, test_do_autostudy);
+ SUITE_ADD_TEST(suite, test_autostudy_batches);
SUITE_ADD_TEST(suite, test_autostudy_run_noteachers);
SUITE_ADD_TEST(suite, test_autostudy_run_teachers_learn);
SUITE_ADD_TEST(suite, test_autostudy_run_twoteachers);
diff --git a/src/battle.c b/src/battle.c
index 132783ecb..5384d80f5 100644
--- a/src/battle.c
+++ b/src/battle.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/bind_region.c b/src/bind_region.c
index 414752cf6..29c7e6893 100644
--- a/src/bind_region.c
+++ b/src/bind_region.c
@@ -527,7 +527,7 @@ static int tolua_region_get_peasants(lua_State * L)
region *self = (region *)tolua_tousertype(L, 1, NULL);
if (self) {
- lua_pushinteger(L, self->land ? self->land->peasants : 0);
+ lua_pushinteger(L, rpeasants(self));
return 1;
}
return 0;
@@ -538,7 +538,7 @@ static int tolua_region_set_peasants(lua_State * L)
region *self = (region *)tolua_tousertype(L, 1, NULL);
if (self && self->land) {
- self->land->peasants = lua_tointeger(L, 2);
+ rsetpeasants(self, lua_tointeger(L, 2));
}
return 0;
}
diff --git a/src/chaos.c b/src/chaos.c
index fd87d87d4..26a11b8a2 100644
--- a/src/chaos.c
+++ b/src/chaos.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/contact.c b/src/contact.c
index 635bdbd3d..2cc320709 100644
--- a/src/contact.c
+++ b/src/contact.c
@@ -115,13 +115,13 @@ int contact_cmd(unit * u, order * ord)
}
else {
/* old-style syntax, KONTAKTIERE foo */
- unit *u2;
+ unit *u2 = NULL;
int n = 0;
if (p == P_TEMP) {
n = getid();
u2 = findnewunit(u->region, u->faction, n);
}
- else {
+ else if (str) {
n = atoi36((const char *)str);
u2 = findunit(n);
}
diff --git a/src/contact.test.c b/src/contact.test.c
index 3a9cc355e..8c76ec7c7 100644
--- a/src/contact.test.c
+++ b/src/contact.test.c
@@ -74,11 +74,73 @@ static void test_contact_cmd(CuTest *tc) {
test_teardown();
}
+static void test_contact_cmd_invalid(CuTest *tc) {
+ struct unit *u;
+ struct region *r;
+ const struct locale *lang;
+ struct order *ord;
+
+ test_setup();
+ r = test_create_plain(0, 0);
+ u = test_create_unit(test_create_faction(NULL), r);
+ lang = u->faction->locale;
+
+ /* KONTAKTIERE EINHEIT */
+ ord = create_order(K_CONTACT, u->faction->locale, "%s %i",
+ LOC(lang, parameters[P_UNIT]), u->no + 1);
+ contact_cmd(u, ord);
+ CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "feedback_unit_not_found"));
+ free_order(ord);
+ test_clear_messages(u->faction);
+
+ /* KONTAKTIERE EINHEIT TEMP */
+ ord = create_order(K_CONTACT, u->faction->locale, "%s %s %i",
+ LOC(lang, parameters[P_UNIT]), LOC(lang, parameters[P_TEMP]), u->no + 1);
+ contact_cmd(u, ord);
+ CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "feedback_unit_not_found"));
+ free_order(ord);
+ test_clear_messages(u->faction);
+
+ /* KONTAKTIERE EINHEIT TEMP */
+ ord = create_order(K_CONTACT, u->faction->locale, "%s %s",
+ LOC(lang, parameters[P_UNIT]), LOC(lang, parameters[P_TEMP]));
+ contact_cmd(u, ord);
+ CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "feedback_unit_not_found"));
+ free_order(ord);
+ test_clear_messages(u->faction);
+
+ /* KONTAKTIERE EINHEIT */
+ ord = create_order(K_CONTACT, u->faction->locale,
+ LOC(lang, parameters[P_UNIT]));
+ contact_cmd(u, ord);
+ CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "feedback_unit_not_found"));
+ free_order(ord);
+ test_clear_messages(u->faction);
+
+ /* KONTAKTIERE TEMP */
+ ord = create_order(K_CONTACT, u->faction->locale,
+ LOC(lang, parameters[P_TEMP]));
+ contact_cmd(u, ord);
+ CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "feedback_unit_not_found"));
+ free_order(ord);
+ test_clear_messages(u->faction);
+
+ /* KONTAKTIERE */
+ ord = create_order(K_CONTACT, u->faction->locale, NULL);
+ contact_cmd(u, ord);
+ CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "feedback_unit_not_found"));
+ free_order(ord);
+ test_clear_messages(u->faction);
+
+ test_teardown();
+}
+
CuSuite *get_contact_suite(void)
{
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_contact);
SUITE_ADD_TEST(suite, test_contact_cmd);
+ SUITE_ADD_TEST(suite, test_contact_cmd_invalid);
return suite;
}
diff --git a/src/creport.c b/src/creport.c
index fce3b8bff..1d3a32d48 100644
--- a/src/creport.c
+++ b/src/creport.c
@@ -1503,33 +1503,34 @@ static void cr_output_region(FILE * F, report_context * ctx, region * r)
cr_output_messages(F, mlist, f);
}
}
- /* buildings */
- for (b = rbuildings(r); b; b = b->next) {
- int fno = -1;
- u = building_owner(b);
- if (u && !fval(u, UFL_ANON_FACTION)) {
- const faction *sf = visible_faction(f, u);
- fno = sf->no;
- }
- cr_output_building_compat(F, b, u, fno, f);
- }
-
- /* ships */
- for (sh = r->ships; sh; sh = sh->next) {
- int fno = -1;
- u = ship_owner(sh);
- if (u && !fval(u, UFL_ANON_FACTION)) {
- const faction *sf = visible_faction(f, u);
- fno = sf->no;
+ if (r->seen.mode >= seen_lighthouse) {
+ /* buildings */
+ for (b = rbuildings(r); b; b = b->next) {
+ int fno = -1;
+ u = building_owner(b);
+ if (u && !fval(u, UFL_ANON_FACTION)) {
+ const faction *sf = visible_faction(f, u);
+ fno = sf->no;
+ }
+ cr_output_building_compat(F, b, u, fno, f);
}
- cr_output_ship_compat(F, sh, u, fno, f, r);
- }
+ /* ships */
+ for (sh = r->ships; sh; sh = sh->next) {
+ int fno = -1;
+ u = ship_owner(sh);
+ if (u && !fval(u, UFL_ANON_FACTION)) {
+ const faction *sf = visible_faction(f, u);
+ fno = sf->no;
+ }
- /* visible units */
- for (u = r->units; u; u = u->next) {
- if (visible_unit(u, f, stealthmod, r->seen.mode)) {
- cr_output_unit_compat(F, f, u, r->seen.mode);
+ cr_output_ship_compat(F, sh, u, fno, f, r);
+ }
+ /* visible units */
+ for (u = r->units; u; u = u->next) {
+ if (visible_unit(u, f, stealthmod, r->seen.mode)) {
+ cr_output_unit_compat(F, f, u, r->seen.mode);
+ }
}
}
}
diff --git a/src/economy.c b/src/economy.c
index efca9ce5c..829b205a1 100644
--- a/src/economy.c
+++ b/src/economy.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2014,
+Copyright (c) 1998-2019,
Enno Rehling
Katja Zedel
@@ -84,28 +84,28 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include
#include
-static int working;
-
-static econ_request entertainers[1024];
-static econ_request *nextentertainer;
-static int entertaining;
+#define MAX_REQUESTS 1024
+static struct econ_request econ_requests[MAX_REQUESTS];
static econ_request **g_requests; /* TODO: no need for this to be module-global */
-#define RECRUIT_MERGE 1
-static int rules_recruit = -1;
+#define ENTERTAINFRACTION 20
-static void recruit_init(void)
-{
- if (rules_recruit < 0) {
- rules_recruit = 0;
- if (config_get_int("recruit.allow_merge", 1)) {
- rules_recruit |= RECRUIT_MERGE;
- }
- }
+static void add_request(econ_request * req, enum econ_type type, unit *u, order *ord, int want) {
+ req->next = NULL;
+ req->unit = u;
+ req->qty = u->wants = want;
+ req->type = type;
}
-#define ENTERTAINFRACTION 20
+static bool rule_auto_taxation(void)
+{
+ return config_get_int("rules.economy.taxation", 0) != 0;
+}
+
+static bool rule_autowork(void) {
+ return config_get_int("work.auto", 0) != 0;
+}
int entertainmoney(const region * r)
{
@@ -187,6 +187,10 @@ int expand_production(region * r, econ_request * requests, econ_request ***resul
return norders;
}
+static int expandorders(region * r, econ_request * requests) {
+ return expand_production(r, requests, &g_requests);
+}
+
static void free_requests(econ_request *requests) {
while (requests) {
econ_request *req = requests->next;
@@ -195,383 +199,8 @@ static void free_requests(econ_request *requests) {
}
}
-static int expandorders(region * r, econ_request * requests) {
- return expand_production(r, requests, &g_requests);
-}
-
/* ------------------------------------------------------------- */
-typedef struct recruitment {
- struct recruitment *next;
- faction *f;
- econ_request *requests;
- int total, assigned;
-} recruitment;
-
-/** Creates a list of recruitment structs, one for each faction. Adds every quantifyable production
- * to the faction's struct and to total.
- */
-static recruitment *select_recruitment(econ_request ** rop,
- int(*quantify) (const struct race *, int), int *total)
-{
- recruitment *recruits = NULL;
-
- while (*rop) {
- recruitment *rec = recruits;
- econ_request *ro = *rop;
- unit *u = ro->unit;
- const race *rc = u_race(u);
- int qty = quantify(rc, ro->qty);
-
- if (qty < 0) {
- rop = &ro->next; /* skip this one */
- }
- else {
- *rop = ro->next; /* remove this one */
- while (rec && rec->f != u->faction)
- rec = rec->next;
- if (rec == NULL) {
- rec = (recruitment *)malloc(sizeof(recruitment));
- if (!rec) abort();
- rec->f = u->faction;
- rec->total = 0;
- rec->assigned = 0;
- rec->requests = NULL;
- rec->next = recruits;
- recruits = rec;
- }
- *total += qty;
- rec->total += qty;
- ro->next = rec->requests;
- rec->requests = ro;
- }
- }
- return recruits;
-}
-
-void add_recruits(unit * u, int number, int wanted)
-{
- region *r = u->region;
- assert(number <= wanted);
- if (number > 0) {
- unit *unew;
- char equipment[64];
- int len;
-
- if (u->number == 0) {
- set_number(u, number);
- u->hp = number * unit_max_hp(u);
- unew = u;
- }
- else {
- unew = create_unit(r, u->faction, number, u_race(u), 0, NULL, u);
- }
-
- len = snprintf(equipment, sizeof(equipment), "new_%s", u_race(u)->_name);
- if (len > 0 && (size_t)len < sizeof(equipment)) {
- equip_unit(unew, equipment);
- }
- if (unew != u) {
- transfermen(unew, u, unew->number);
- remove_unit(&r->units, unew);
- }
- }
- if (number < wanted) {
- ADDMSG(&u->faction->msgs, msg_message("recruit",
- "unit region amount want", u, r, number, wanted));
- }
-}
-
-static int any_recruiters(const struct race *rc, int qty)
-{
- return (int)(qty * 2 * rc->recruit_multi);
-}
-
-static int do_recruiting(recruitment * recruits, int available)
-{
- recruitment *rec;
- int recruited = 0;
-
- /* try to assign recruits to factions fairly */
- while (available > 0) {
- int n = 0;
- int rest, mintotal = INT_MAX;
-
- /* find smallest production */
- for (rec = recruits; rec != NULL; rec = rec->next) {
- int want = rec->total - rec->assigned;
- if (want > 0) {
- if (mintotal > want)
- mintotal = want;
- ++n;
- }
- }
- if (n == 0)
- break;
- if (mintotal * n > available) {
- mintotal = available / n;
- }
- rest = available - mintotal * n;
-
- /* assign size of smallest production for everyone if possible; in the end roll dice to assign
- * small rest */
- for (rec = recruits; rec != NULL; rec = rec->next) {
- int want = rec->total - rec->assigned;
-
- if (want > 0) {
- int get = mintotal;
- if (want > mintotal && rest < n && (rng_int() % n) < rest) {
- --rest;
- ++get;
- }
- assert(get <= want);
- available -= get;
- rec->assigned += get;
- }
- }
- }
-
- /* do actual recruiting */
- for (rec = recruits; rec != NULL; rec = rec->next) {
- econ_request *req;
- int get = rec->assigned;
-
- for (req = rec->requests; req; req = req->next) {
- unit *u = req->unit;
- const race *rc = u_race(u); /* race is set in recruit() */
- int number;
- double multi = 2.0 * rc->recruit_multi;
-
- number = (int)(get / multi);
- if (number > req->qty) number = req->qty;
- if (rc->recruitcost) {
- int afford = get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT,
- number * rc->recruitcost) / rc->recruitcost;
- if (number > afford) number = afford;
- }
- if (u->number + number > UNIT_MAXSIZE) {
- ADDMSG(&u->faction->msgs, msg_feedback(u, req->type.recruit.ord, "error_unit_size",
- "maxsize", UNIT_MAXSIZE));
- number = UNIT_MAXSIZE - u->number;
- assert(number >= 0);
- }
- if (rc->recruitcost) {
- use_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT,
- rc->recruitcost * number);
- }
- if (u->number == 0 && fval(u, UFL_DEAD)) {
- /* unit is empty, dead, and cannot recruit */
- number = 0;
- }
- add_recruits(u, number, req->qty);
- if (number > 0) {
- int dec = (int)(number * multi);
- if ((rc->ec_flags & ECF_REC_ETHEREAL) == 0) {
- recruited += dec;
- }
-
- get -= dec;
- }
- }
- }
- return recruited;
-}
-
-void free_recruitments(recruitment * recruits)
-{
- while (recruits) {
- recruitment *rec = recruits;
- recruits = rec->next;
- while (rec->requests) {
- econ_request *req = rec->requests;
- rec->requests = req->next;
- free(req);
- }
- free(rec);
- }
-}
-
-/* Rekrutierung */
-static void expandrecruit(region * r, econ_request * recruitorders)
-{
- recruitment *recruits;
- int orc_total = 0;
-
- /* peasant limited: */
- recruits = select_recruitment(&recruitorders, any_recruiters, &orc_total);
- if (recruits) {
- int orc_recruited, orc_peasants = rpeasants(r) * 2;
- int orc_frac = orc_peasants / RECRUITFRACTION; /* anzahl orks. 2 ork = 1 bauer */
- if (orc_total < orc_frac)
- orc_frac = orc_total;
- orc_recruited = do_recruiting(recruits, orc_frac);
- assert(orc_recruited <= orc_frac);
- rsetpeasants(r, (orc_peasants - orc_recruited) / 2);
- free_recruitments(recruits);
- }
-
- /* no limit: */
- recruits = select_recruitment(&recruitorders, any_recruiters, &orc_total);
- if (recruits) {
- int recruited, peasants = rpeasants(r) * 2;
- recruited = do_recruiting(recruits, INT_MAX);
- if (recruited > 0) {
- rsetpeasants(r, (peasants - recruited) / 2);
- }
- free_recruitments(recruits);
- }
-
- assert(recruitorders == NULL);
-}
-
-static int recruit_cost(const faction * f, const race * rc)
-{
- if (is_monsters(f) || valid_race(f, rc)) {
- return rc->recruitcost;
- }
- return -1;
-}
-
-message *can_recruit(unit *u, const race *rc, order *ord, int now)
-{
- region *r = u->region;
-
- /* this is a very special case because the recruiting unit may be empty
- * at this point and we have to look at the creating unit instead. This
- * is done in cansee, which is called indirectly by is_guarded(). */
- if (is_guarded(r, u)) {
- return msg_error(u, ord, 70);
- }
-
- if (rc == get_race(RC_INSECT)) {
- gamedate date;
- get_gamedate(now, &date);
- if (date.season == SEASON_WINTER && r->terrain != newterrain(T_DESERT)) {
- bool usepotion = false;
- unit *u2;
-
- for (u2 = r->units; u2; u2 = u2->next) {
- if (fval(u2, UFL_WARMTH)) {
- usepotion = true;
- break;
- }
- }
- if (!usepotion) {
- return msg_error(u, ord, 98);
- }
- }
- /* in Gletschern, Eisbergen gar nicht rekrutieren */
- if (r_insectstalled(r)) {
- return msg_error(u, ord, 97);
- }
- }
- if (is_cursed(r->attribs, &ct_riotzone)) {
- /* Die Region befindet sich in Aufruhr */
- return msg_error(u, ord, 237);
- }
-
- if (rc && !playerrace(rc)) {
- return msg_error(u, ord, 139);
- }
-
- if (fval(u, UFL_HERO)) {
- return msg_feedback(u, ord, "error_herorecruit", "");
- }
- if (has_skill(u, SK_MAGIC)) {
- /* error158;de;{unit} in {region}: '{command}' - Magier arbeiten
- * grundsaetzlich nur alleine! */
- return msg_error(u, ord, 158);
- }
- return NULL;
-}
-
-static void recruit(unit * u, struct order *ord, econ_request ** recruitorders)
-{
- region *r = u->region;
- econ_request *o;
- int recruitcost = -1;
- const faction *f = u->faction;
- const struct race *rc = u_race(u);
- int n;
- message *msg;
-
- init_order_depr(ord);
- n = getint();
- if (n <= 0) {
- syntax_error(u, ord);
- return;
- }
-
- if (u->number == 0) {
- char token[128];
- const char *str;
-
- str = gettoken(token, sizeof(token));
- if (str && str[0]) {
- /* Monsters can RECRUIT 15 DRACOID
- * also: secondary race */
- rc = findrace(str, f->locale);
- if (rc != NULL) {
- recruitcost = recruit_cost(f, rc);
- }
- }
- }
-
- if (recruitcost < 0) {
- rc = u_race(u);
- recruitcost = recruit_cost(f, rc);
- if (recruitcost < 0) {
- recruitcost = INT_MAX;
- }
- }
-
- if (recruitcost > 0) {
- int pool;
- plane *pl = getplane(r);
-
- if (pl && (pl->flags & PFL_NORECRUITS)) {
- ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_pflnorecruit", ""));
- return;
- }
-
- pool = get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT, recruitcost * n);
- if (pool < recruitcost) {
- cmistake(u, ord, 142, MSG_EVENT);
- return;
- }
- pool /= recruitcost;
- if (n > pool) n = pool;
- }
-
- if (!n) {
- cmistake(u, ord, 142, MSG_EVENT);
- return;
- }
- if (has_skill(u, SK_ALCHEMY)) {
- if (faction_count_skill(u->faction, SK_ALCHEMY) + n > faction_skill_limit(u->faction, SK_ALCHEMY)) {
- cmistake(u, ord, 156, MSG_EVENT);
- return;
- }
- }
-
- assert(rc);
- msg = can_recruit(u, rc, ord, turn);
- if (msg) {
- add_message(&u->faction->msgs, msg);
- msg_release(msg);
- return;
- }
-
- u_setrace(u, rc);
- u->wants = n;
- o = (econ_request *)calloc(1, sizeof(econ_request));
- if (!o) abort();
- o->qty = n;
- o->unit = u;
- o->type.recruit.ord = ord;
- addlist(recruitorders, o);
-}
-
static void friendly_takeover(region * r, faction * f)
{
region_set_owner(r, f, turn);
@@ -761,7 +390,6 @@ void maintain_buildings(region * r)
void economics(region * r)
{
unit *u;
- econ_request *recruitorders = NULL;
/* Geben vor Selbstmord (doquit)! Hier alle unmittelbaren Befehle.
* Rekrutieren vor allen Einnahmequellen. Bewachen JA vor Steuern
@@ -784,28 +412,10 @@ void economics(region * r)
}
}
}
- /* RECRUIT orders */
-
- if (rules_recruit < 0)
- recruit_init();
- for (u = r->units; u; u = u->next) {
- order *ord;
-
- if ((rules_recruit & RECRUIT_MERGE) || u->number == 0) {
- for (ord = u->orders; ord; ord = ord->next) {
- if (getkeyword(ord) == K_RECRUIT) {
- recruit(u, ord, &recruitorders);
- break;
- }
- }
- }
- }
-
- if (recruitorders) {
- expandrecruit(r, recruitorders);
- }
- remove_empty_units_in_region(r);
+}
+void destroy(region *r) {
+ unit *u;
for (u = r->units; u; u = u->next) {
order *ord = u->thisorder;
keyword_t kwd = getkeyword(ord);
@@ -1490,7 +1100,7 @@ static void expandbuying(region * r, econ_request * buyorders)
unsigned int j;
for (j = 0; j != norders; j++) {
int price, multi;
- ltype = g_requests[j]->type.trade.ltype;
+ ltype = g_requests[j]->data.trade.ltype;
trade = trades;
while (trade->type && trade->type != ltype)
++trade;
@@ -1671,10 +1281,11 @@ static void buy(unit * u, econ_request ** buyorders, struct order *ord)
}
o = (econ_request *)calloc(1, sizeof(econ_request));
if (!o) abort();
- o->type.trade.ltype = ltype; /* sollte immer gleich sein */
+ o->data.trade.ltype = ltype; /* sollte immer gleich sein */
o->unit = u;
o->qty = n;
+ o->type = ECON_BUY;
addlist(buyorders, o);
}
@@ -1774,7 +1385,7 @@ static void expandselling(region * r, econ_request * sellorders, int limit)
int j;
for (j = 0; j != norders; j++) {
const luxury_type *search = NULL;
- const luxury_type *ltype = g_requests[j]->type.trade.ltype;
+ const luxury_type *ltype = g_requests[j]->data.trade.ltype;
int multi = r_demand(r, ltype);
int i, price;
int use = 0;
@@ -1981,7 +1592,7 @@ static bool sell(unit * u, econ_request ** sellorders, struct order *ord)
/* Wenn andere Einheiten das selbe verkaufen, muss ihr Zeug abgezogen
* werden damit es nicht zweimal verkauft wird: */
for (o = *sellorders; o; o = o->next) {
- if (o->type.trade.ltype == ltype && o->unit->faction == u->faction) {
+ if (o->data.trade.ltype == ltype && o->unit->faction == u->faction) {
int fpool =
o->qty - get_pooled(o->unit, itype->rtype, GET_RESERVE, INT_MAX);
if (fpool < 0) fpool = 0;
@@ -2022,7 +1633,8 @@ static bool sell(unit * u, econ_request ** sellorders, struct order *ord)
if (!o) abort();
o->unit = u;
o->qty = n;
- o->type.trade.ltype = ltype;
+ o->type = ECON_SELL;
+ o->data.trade.ltype = ltype;
addlist(sellorders, o);
return unlimited;
@@ -2349,36 +1961,39 @@ static void research_cmd(unit * u, struct order *ord)
}
}
-static void expandentertainment(region * r)
+static void expandentertainment(region * r, econ_request *ecbegin, econ_request *ecend, long total)
{
int m = entertainmoney(r);
econ_request *o;
- for (o = &entertainers[0]; o != nextentertainer; ++o) {
- double part = m / (double)entertaining;
- unit *u = o->unit;
+ for (o = ecbegin; o != ecend; ++o) {
+ if (o->type == ECON_ENTERTAIN) {
+ double part = m / (double)total;
+ unit *u = o->unit;
- if (entertaining <= m)
- u->n = o->qty;
- else
- u->n = (int)(o->qty * part);
- change_money(u, u->n);
- rsetmoney(r, rmoney(r) - u->n);
- m -= u->n;
- entertaining -= o->qty;
+ if (total <= m)
+ u->n = o->qty;
+ else
+ u->n = (int)(o->qty * part);
+ change_money(u, u->n);
+ rsetmoney(r, rmoney(r) - u->n);
+ m -= u->n;
+ total -= o->qty;
- /* Nur soviel PRODUCEEXP wie auch tatsaechlich gemacht wurde */
- produceexp(u, SK_ENTERTAINMENT, (u->n < u->number) ? u->n : u->number);
- add_income(u, IC_ENTERTAIN, o->qty, u->n);
- fset(u, UFL_LONGACTION | UFL_NOTMOVING);
+ /* Nur soviel PRODUCEEXP wie auch tatsaechlich gemacht wurde */
+ produceexp(u, SK_ENTERTAINMENT, (u->n < u->number) ? u->n : u->number);
+ add_income(u, IC_ENTERTAIN, o->qty, u->n);
+ fset(u, UFL_LONGACTION | UFL_NOTMOVING);
+ }
}
+ assert(total == 0);
}
-void entertain_cmd(unit * u, struct order *ord)
+static int entertain_cmd(unit * u, struct order *ord, econ_request **io_req)
{
region *r = u->region;
- int max_e;
- econ_request *o;
+ int wants, max_e;
+ econ_request *req = *io_req;
static int entertainbase = 0;
static int entertainperlevel = 0;
keyword_t kwd;
@@ -2395,39 +2010,38 @@ void entertain_cmd(unit * u, struct order *ord)
}
if (fval(u, UFL_WERE)) {
cmistake(u, ord, 58, MSG_INCOME);
- return;
+ return 0;
}
if (!effskill(u, SK_ENTERTAINMENT, NULL)) {
cmistake(u, ord, 58, MSG_INCOME);
- return;
+ return 0;
}
if (u->ship && is_guarded(r, u)) {
cmistake(u, ord, 69, MSG_INCOME);
- return;
+ return 0;
}
if (is_cursed(r->attribs, &ct_depression)) {
cmistake(u, ord, 28, MSG_INCOME);
- return;
+ return 0;
}
- u->wants = u->number * (entertainbase + effskill(u, SK_ENTERTAINMENT, NULL)
- * entertainperlevel);
-
- max_e = getuint();
- if (max_e != 0) {
- if (u->wants > max_e) u->wants = max_e;
+ wants = getuint();
+ max_e = u->number * (entertainbase + effskill(u, SK_ENTERTAINMENT, NULL) * entertainperlevel);
+ if (wants > 0 && wants < max_e) {
+ max_e = wants;
}
- o = nextentertainer++;
- o->unit = u;
- o->qty = u->wants;
- entertaining += o->qty;
+ if (max_e > 0) {
+ add_request(req++, ECON_ENTERTAIN, u, ord, max_e);
+ *io_req = req;
+ }
+ return max_e;
}
/**
* \return number of working spaces taken by players
*/
static void
-expandwork(region * r, econ_request * work_begin, econ_request * work_end, int maxwork)
+expandwork(region * r, econ_request * work_begin, econ_request * work_end, int maxwork, long total)
{
int earnings;
/* n: verbleibende Einnahmen */
@@ -2435,37 +2049,41 @@ expandwork(region * r, econ_request * work_begin, econ_request * work_end, int m
int jobs = maxwork;
int p_wage = wage(r, NULL, NULL, turn);
int money = rmoney(r);
- econ_request *o;
+ if (total > 0 && !rule_autowork()) {
+ econ_request *o;
- for (o = work_begin; o != work_end; ++o) {
- unit *u = o->unit;
- int workers;
+ for (o = work_begin; o != work_end; ++o) {
+ if (o->type == ECON_WORK) {
+ unit *u = o->unit;
+ int workers;
- if (u->number == 0)
- continue;
+ if (u->number == 0)
+ continue;
- if (jobs >= working)
- workers = u->number;
- else {
- int req = (u->number * jobs) % working;
- workers = u->number * jobs / working;
- if (req > 0 && rng_int() % working < req)
- workers++;
+ if (jobs >= total)
+ workers = u->number;
+ else {
+ int req = (u->number * jobs) % total;
+ workers = u->number * jobs / total;
+ if (req > 0 && rng_int() % total < req)
+ workers++;
+ }
+
+ assert(workers >= 0);
+
+ u->n = workers * wage(u->region, u->faction, u_race(u), turn);
+
+ jobs -= workers;
+ assert(jobs >= 0);
+
+ change_money(u, u->n);
+ total -= o->unit->number;
+ add_income(u, IC_WORK, o->qty, u->n);
+ fset(u, UFL_LONGACTION | UFL_NOTMOVING);
+ }
}
-
- assert(workers >= 0);
-
- u->n = workers * wage(u->region, u->faction, u_race(u), turn);
-
- jobs -= workers;
- assert(jobs >= 0);
-
- change_money(u, u->n);
- working -= o->unit->number;
- add_income(u, IC_WORK, o->qty, u->n);
- fset(u, UFL_LONGACTION | UFL_NOTMOVING);
+ assert(total == 0);
}
-
if (jobs > rpeasants(r)) {
jobs = rpeasants(r);
}
@@ -2478,34 +2096,35 @@ expandwork(region * r, econ_request * work_begin, econ_request * work_end, int m
rsetmoney(r, money + earnings);
}
-static int do_work(unit * u, order * ord, econ_request * o)
+static int work_cmd(unit * u, order * ord, econ_request ** io_req)
{
if (playerrace(u_race(u))) {
+ econ_request *req = *io_req;
region *r = u->region;
int w;
if (fval(u, UFL_WERE)) {
- if (ord)
+ if (ord) {
cmistake(u, ord, 313, MSG_INCOME);
- return -1;
+ }
+ return 0;
}
if (u->ship && is_guarded(r, u)) {
- if (ord)
+ if (ord) {
cmistake(u, ord, 69, MSG_INCOME);
- return -1;
+ }
+ return 0;
}
w = wage(r, u->faction, u_race(u), turn);
- u->wants = u->number * w;
- o->unit = u;
- o->qty = u->number * w;
- working += u->number;
- return 0;
+ add_request(req++, ECON_WORK, u, ord, w * u->number);
+ *io_req = req;
+ return u->number;
}
else if (ord && !is_monsters(u->faction)) {
ADDMSG(&u->faction->msgs,
msg_feedback(u, ord, "race_cantwork", "race", u_race(u)));
}
- return -1;
+ return 0;
}
static void expandloot(region * r, econ_request * lootorders)
@@ -2637,6 +2256,7 @@ void tax_cmd(unit * u, struct order *ord, econ_request ** taxorders)
o = (econ_request *)calloc(1, sizeof(econ_request));
if (!o) abort();
o->qty = u->wants / TAXFRACTION;
+ o->type = ECON_TAX;
o->unit = u;
addlist(taxorders, o);
return;
@@ -2703,29 +2323,30 @@ void loot_cmd(unit * u, struct order *ord, econ_request ** lootorders)
o = (econ_request *)calloc(1, sizeof(econ_request));
if (!o) abort();
o->qty = u->wants / TAXFRACTION;
+ o->type = ECON_LOOT;
o->unit = u;
addlist(lootorders, o);
return;
}
-#define MAX_WORKERS 512
void auto_work(region * r)
{
- econ_request workers[MAX_WORKERS];
- econ_request *nextworker = workers;
+ econ_request *nextrequest = econ_requests;
unit *u;
+ long total = 0;
for (u = r->units; u; u = u->next) {
if (!(u->flags & UFL_LONGACTION) && !is_monsters(u->faction)) {
- if (do_work(u, NULL, nextworker) == 0) {
- assert(nextworker - workers < MAX_WORKERS);
- ++nextworker;
+ int work = work_cmd(u, NULL, &nextrequest);
+ if (work) {
+ total += work;
+ assert(nextrequest - econ_requests <= MAX_REQUESTS);
}
}
}
- if (nextworker != workers) {
- expandwork(r, workers, nextworker, region_maxworkers(r));
+ if (nextrequest != econ_requests) {
+ expandwork(r, econ_requests, nextrequest, region_maxworkers(r), total);
}
}
@@ -2770,22 +2391,13 @@ static void peasant_taxes(region * r)
}
}
-static bool rule_auto_taxation(void)
-{
- return config_get_int("rules.economy.taxation", 0) != 0;
-}
-
-static bool rule_autowork(void) {
- return config_get_int("work.auto", 0) != 0;
-}
-
void produce(struct region *r)
{
- econ_request workers[MAX_WORKERS];
econ_request *taxorders, *lootorders, *sellorders, *stealorders, *buyorders;
unit *u;
bool limited = true;
- econ_request *nextworker = workers;
+ long entertaining = 0, working = 0;
+ econ_request *nextrequest = econ_requests;
static int bt_cache;
static const struct building_type *caravan_bt;
static int rc_cache;
@@ -2820,9 +2432,6 @@ void produce(struct region *r)
buyorders = 0;
sellorders = 0;
- working = 0;
- nextentertainer = &entertainers[0];
- entertaining = 0;
taxorders = 0;
lootorders = 0;
stealorders = 0;
@@ -2879,13 +2488,17 @@ void produce(struct region *r)
switch (todo) {
case K_ENTERTAIN:
- entertain_cmd(u, u->thisorder);
+ entertaining += entertain_cmd(u, u->thisorder, &nextrequest);
+ assert(nextrequest - econ_requests <= MAX_REQUESTS);
break;
case K_WORK:
- if (!rule_autowork() && do_work(u, u->thisorder, nextworker) == 0) {
- assert(nextworker - workers < MAX_WORKERS);
- ++nextworker;
+ if (!rule_autowork()) {
+ int work = work_cmd(u, u->thisorder, &nextrequest);
+ if (work != 0) {
+ working += work;
+ assert(nextrequest - econ_requests <= MAX_REQUESTS);
+ }
}
break;
@@ -2927,11 +2540,11 @@ void produce(struct region *r)
* Befehlen, die den Bauern mehr Geld geben, damit man aus den Zahlen der
* letzten Runde berechnen kann, wieviel die Bauern fuer Unterhaltung
* auszugeben bereit sind. */
- if (entertaining)
- expandentertainment(r);
- if (!rule_autowork()) {
- expandwork(r, workers, nextworker, region_maxworkers(r));
+ if (entertaining > 0) {
+ expandentertainment(r, econ_requests, nextrequest, entertaining);
}
+ expandwork(r, econ_requests, nextrequest, region_maxworkers(r), working);
+
if (taxorders) {
expandtax(r, taxorders);
free_requests(taxorders);
diff --git a/src/economy.h b/src/economy.h
index 9456a4c93..94c0997c8 100644
--- a/src/economy.h
+++ b/src/economy.h
@@ -55,10 +55,17 @@ extern "C" {
struct econ_request *next;
struct unit *unit;
int qty;
+ enum econ_type {
+ ECON_LIST,
+ ECON_ENTERTAIN,
+ ECON_WORK,
+ ECON_TAX,
+ ECON_LOOT,
+ ECON_BUY,
+ ECON_SELL,
+ ECON_STEAL,
+ } type;
union {
- struct {
- struct order *ord;
- } recruit;
struct {
int no;
bool goblin; /* stealing */
@@ -66,18 +73,19 @@ extern "C" {
struct {
const struct luxury_type *ltype; /* trading */
} trade;
- } type;
+ } data;
} econ_request;
+ int expand_production(struct region * r, struct econ_request * requests, struct econ_request ***results);
+
int income(const struct unit *u);
int entertainmoney(const struct region *r);
void economics(struct region *r);
+ void destroy(struct region *r);
void produce(struct region *r);
void auto_work(struct region *r);
- int expand_production(struct region * r, struct econ_request * requests, struct econ_request ***results);
-
typedef enum income_t { IC_WORK, IC_ENTERTAIN, IC_TAX, IC_TRADE, IC_TRADETAX, IC_STEAL, IC_MAGIC, IC_LOOT } income_t;
void add_income(struct unit * u, income_t type, int want, int qty);
@@ -95,9 +103,6 @@ extern "C" {
void steal_cmd(struct unit * u, struct order *ord, struct econ_request ** stealorders);
void expandstealing(struct region * r, struct econ_request * stealorders);
- struct message *can_recruit(struct unit *u, const struct race *rc, struct order *ord, int now);
- void add_recruits(struct unit * u, int number, int wanted);
-
#ifdef __cplusplus
}
#endif
diff --git a/src/economy.test.c b/src/economy.test.c
index 65059ffb2..024ccadbd 100644
--- a/src/economy.test.c
+++ b/src/economy.test.c
@@ -3,6 +3,7 @@
#endif
#include
#include "economy.h"
+#include "recruit.h"
#include
#include
@@ -162,7 +163,7 @@ static void test_heroes_dont_recruit(CuTest * tc) {
fset(u, UFL_HERO);
unit_addorder(u, create_order(K_RECRUIT, default_locale, "1"));
- economics(u->region);
+ recruit(u->region);
CuAssertIntEquals(tc, 1, u->number);
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error_herorecruit"));
@@ -178,7 +179,7 @@ static void test_normals_recruit(CuTest * tc) {
u = create_recruiter();
unit_addorder(u, create_order(K_RECRUIT, default_locale, "1"));
- economics(u->region);
+ recruit(u->region);
CuAssertIntEquals(tc, 2, u->number);
diff --git a/src/guard.c b/src/guard.c
index 437980b34..b27423b2e 100644
--- a/src/guard.c
+++ b/src/guard.c
@@ -1,6 +1,5 @@
/*
-Copyright (c) 1998-2015,
-Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/items/speedsail.c b/src/items/speedsail.c
index e6d3d1386..69c7deed0 100644
--- a/src/items/speedsail.c
+++ b/src/items/speedsail.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/items/weapons.c b/src/items/weapons.c
index 040fa0293..bb8191f52 100644
--- a/src/items/weapons.c
+++ b/src/items/weapons.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/items/xerewards.c b/src/items/xerewards.c
index c49787ec7..78430693c 100644
--- a/src/items/xerewards.c
+++ b/src/items/xerewards.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/kernel/attrib.c b/src/kernel/attrib.c
index 8b3e1c958..7f65e4b73 100644
--- a/src/kernel/attrib.c
+++ b/src/kernel/attrib.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/kernel/build.c b/src/kernel/build.c
index 14d07bb55..271960907 100644
--- a/src/kernel/build.c
+++ b/src/kernel/build.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/kernel/building.c b/src/kernel/building.c
index 823ad1936..fd81651be 100644
--- a/src/kernel/building.c
+++ b/src/kernel/building.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/kernel/config.c b/src/kernel/config.c
index 5c0bfc777..f620cb882 100644
--- a/src/kernel/config.c
+++ b/src/kernel/config.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/kernel/connection.c b/src/kernel/connection.c
index d85f71910..e80294909 100644
--- a/src/kernel/connection.c
+++ b/src/kernel/connection.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/kernel/curse.c b/src/kernel/curse.c
index fbe9ed15d..be7ca4559 100644
--- a/src/kernel/curse.c
+++ b/src/kernel/curse.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/kernel/equipment.c b/src/kernel/equipment.c
index 39e59bf43..4042662c2 100644
--- a/src/kernel/equipment.c
+++ b/src/kernel/equipment.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/kernel/event.c b/src/kernel/event.c
index 9e04abb6a..a20296c57 100644
--- a/src/kernel/event.c
+++ b/src/kernel/event.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/kernel/faction.c b/src/kernel/faction.c
index 86f74dc03..b5067b7cf 100755
--- a/src/kernel/faction.c
+++ b/src/kernel/faction.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
@@ -216,6 +216,7 @@ faction *addfaction(const char *email, const char *password,
const struct race * frace, const struct locale * loc)
{
faction *f = calloc(1, sizeof(faction));
+ const char *fname;
char buf[128];
if (!f) abort();
@@ -255,7 +256,8 @@ faction *addfaction(const char *email, const char *password,
addlist(&factions, f);
fhash(f);
- slprintf(buf, sizeof(buf), "%s %s", LOC(loc, "factiondefault"), itoa36(f->no));
+ fname = LOC(loc, "factiondefault");
+ slprintf(buf, sizeof(buf), "%s %s", fname ? fname : "faction", itoa36(f->no));
f->name = str_strdup(buf);
if (!f->race) {
@@ -300,7 +302,7 @@ unit *addplayer(region * r, faction * f)
} while (rc == NULL || urc == RC_DAEMON || !playerrace(rc));
u->irace = rc;
}
- f->lastorders = 0;
+ f->lastorders = turn;
return u;
}
diff --git a/src/kernel/faction.test.c b/src/kernel/faction.test.c
index e572cf312..2b7d97a5a 100644
--- a/src/kernel/faction.test.c
+++ b/src/kernel/faction.test.c
@@ -344,6 +344,7 @@ static void test_addplayer(CuTest *tc) {
CuAssertPtrNotNull(tc, u);
CuAssertPtrEquals(tc, r, u->region);
CuAssertPtrEquals(tc, f, u->faction);
+ CuAssertIntEquals(tc, turn, u->faction->lastorders);
CuAssertIntEquals(tc, i_get(u->items, itype), 10);
CuAssertPtrNotNull(tc, u->orders);
CuAssertIntEquals(tc, K_WORK, getkeyword(u->orders));
diff --git a/src/kernel/group.c b/src/kernel/group.c
index bfa654cdf..482eb26f8 100755
--- a/src/kernel/group.c
+++ b/src/kernel/group.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/kernel/item.c b/src/kernel/item.c
index 04d106399..4f33ce9bb 100644
--- a/src/kernel/item.c
+++ b/src/kernel/item.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/kernel/messages.c b/src/kernel/messages.c
index 344a96907..6e31810bf 100644
--- a/src/kernel/messages.c
+++ b/src/kernel/messages.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/kernel/pathfinder.c b/src/kernel/pathfinder.c
index ba37d651d..054fc6c27 100644
--- a/src/kernel/pathfinder.c
+++ b/src/kernel/pathfinder.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/kernel/plane.c b/src/kernel/plane.c
index 4fc3ff816..6d957ef57 100644
--- a/src/kernel/plane.c
+++ b/src/kernel/plane.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/kernel/pool.c b/src/kernel/pool.c
index 4ff2ce6f4..7236aeeed 100644
--- a/src/kernel/pool.c
+++ b/src/kernel/pool.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/kernel/race.c b/src/kernel/race.c
index debf368ef..af023341c 100644
--- a/src/kernel/race.c
+++ b/src/kernel/race.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/kernel/region.c b/src/kernel/region.c
index a0f2ec99e..85cb2a944 100644
--- a/src/kernel/region.c
+++ b/src/kernel/region.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
@@ -625,7 +625,11 @@ void rsetpeasants(region * r, int value)
assert(r->land || value==0);
assert(value >= 0);
if (r->land) {
- r->land->peasants = value;
+ if (value > USHRT_MAX) {
+ log_warning("region %s cannot have %d peasants.", regionname(r, NULL), value);
+ value = USHRT_MAX;
+ }
+ r->land->peasants = (unsigned short)value;
}
}
@@ -639,7 +643,11 @@ void rsethorses(const region * r, int value)
assert(r->land || value==0);
assert(value >= 0);
if (r->land) {
- r->land->horses = value;
+ if (value > USHRT_MAX) {
+ log_warning("region %s cannot have %d horses.", regionname(r, NULL), value);
+ value = USHRT_MAX;
+ }
+ r->land->horses = (unsigned short)value;
}
}
@@ -659,15 +667,18 @@ void rsetmoney(region * r, int value)
int rherbs(const region *r)
{
- return r->land?r->land->herbs:0;
+ return r->land ? r->land->herbs : 0;
}
void rsetherbs(region *r, int value)
{
assert(r->land || value==0);
- assert(value >= 0 && value<=SHRT_MAX);
if (r->land) {
- r->land->herbs = value;
+ if (value > USHRT_MAX) {
+ log_warning("region %s cannot have %d herbs.", regionname(r, NULL), value);
+ value = USHRT_MAX;
+ }
+ r->land->herbs = (unsigned short)value;
}
}
@@ -1478,7 +1489,7 @@ const char *region_getname(const region * r)
int region_get_morale(const region * r)
{
if (r->land) {
- assert(r->land->morale >= 0 && r->land->morale <= MORALE_MAX);
+ assert(r->land->morale <= MORALE_MAX);
return r->land->morale;
}
return -1;
@@ -1487,11 +1498,11 @@ int region_get_morale(const region * r)
void region_set_morale(region * r, int morale, int turn)
{
if (r->land) {
- r->land->morale = morale;
+ r->land->morale = (unsigned short)morale;
if (turn >= 0 && r->land->ownership) {
r->land->ownership->morale_turn = turn;
}
- assert(r->land->morale >= 0 && r->land->morale <= MORALE_MAX);
+ assert(r->land->morale <= MORALE_MAX);
}
}
diff --git a/src/kernel/region.h b/src/kernel/region.h
index 9398919b5..7bd6cb2c4 100644
--- a/src/kernel/region.h
+++ b/src/kernel/region.h
@@ -97,12 +97,12 @@ extern "C" {
char *display;
demand *demands;
const struct item_type *herbtype;
- int herbs;
- int morale;
+ unsigned short horses;
+ unsigned short herbs;
+ unsigned short peasants;
+ unsigned short morale;
+ short newpeasants;
int trees[3]; /* 0 -> seeds, 1 -> shoots, 2 -> trees */
- int horses;
- int peasants;
- int newpeasants;
int money;
struct region_owner *ownership;
} land_region;
diff --git a/src/kernel/save.c b/src/kernel/save.c
index 2b66c7f18..43699391e 100644
--- a/src/kernel/save.c
+++ b/src/kernel/save.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/kernel/ship.c b/src/kernel/ship.c
index 54264e95d..d25e0c911 100644
--- a/src/kernel/ship.c
+++ b/src/kernel/ship.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
@@ -318,7 +318,10 @@ int crew_skill(const ship *sh) {
for (u = sh->region->units; u; u = u->next) {
if (u->ship == sh) {
- n += effskill(u, SK_SAILING, NULL) * u->number;
+ int es = effskill(u, SK_SAILING, NULL);
+ if (es >= sh->type->minskill) {
+ n += es * u->number;
+ }
}
}
return n;
diff --git a/src/kernel/ship.test.c b/src/kernel/ship.test.c
index 71282cbd8..062e7cd20 100644
--- a/src/kernel/ship.test.c
+++ b/src/kernel/ship.test.c
@@ -384,27 +384,6 @@ static void test_stype_defaults(CuTest *tc) {
test_teardown();
}
-static void test_crew_skill(CuTest *tc) {
- ship *sh;
- region *r;
- struct faction *f;
- int i;
-
- test_setup();
- test_create_world();
- r = test_create_region(0, 0, NULL);
- f = test_create_faction(NULL);
- assert(r && f);
- sh = test_create_ship(r, st_find("boat"));
- for (i = 0; i != 4; ++i) {
- unit * u = test_create_unit(f, r);
- set_level(u, SK_SAILING, 5);
- u->ship = sh;
- }
- CuAssertIntEquals(tc, 20, crew_skill(sh));
- test_teardown();
-}
-
static ship *setup_ship(void) {
region *r;
ship_type *stype;
@@ -651,6 +630,35 @@ static void test_shipspeed_max_range(CuTest *tc) {
test_teardown();
}
+static void test_crew_skill(CuTest *tc) {
+ ship_type *stype;
+ ship * sh;
+ unit *u;
+ region *r;
+
+ test_setup();
+ stype = test_create_shiptype("kayak");
+ CuAssertIntEquals(tc, 1, stype->minskill);
+ r = test_create_ocean(0, 0);
+ sh = test_create_ship(r, stype);
+ CuAssertIntEquals(tc, 0, crew_skill(sh));
+ u = test_create_unit(test_create_faction(NULL), r);
+ set_level(u, SK_SAILING, 1);
+ CuAssertIntEquals(tc, 0, crew_skill(sh));
+ u_set_ship(u, sh);
+ set_level(u, SK_SAILING, 1);
+ CuAssertIntEquals(tc, 1, crew_skill(sh));
+ set_number(u, 10);
+ CuAssertIntEquals(tc, 10, crew_skill(sh));
+ stype->minskill = 2;
+ CuAssertIntEquals(tc, 0, crew_skill(sh));
+ set_level(u, SK_SAILING, 2);
+ CuAssertIntEquals(tc, 20, crew_skill(sh));
+ set_level(u, SK_SAILING, 3);
+ CuAssertIntEquals(tc, 30, crew_skill(sh));
+ test_teardown();
+}
+
CuSuite *get_ship_suite(void)
{
CuSuite *suite = CuSuiteNew();
diff --git a/src/kernel/skills.c b/src/kernel/skills.c
index 4fba7284f..7987bb75c 100644
--- a/src/kernel/skills.c
+++ b/src/kernel/skills.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/kernel/spell.c b/src/kernel/spell.c
index 8cc56d863..96190717f 100644
--- a/src/kernel/spell.c
+++ b/src/kernel/spell.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/kernel/terrain.c b/src/kernel/terrain.c
index 3f2390254..57598c43c 100644
--- a/src/kernel/terrain.c
+++ b/src/kernel/terrain.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/kernel/types.h b/src/kernel/types.h
index b67ee088a..82af31cdc 100644
--- a/src/kernel/types.h
+++ b/src/kernel/types.h
@@ -59,6 +59,7 @@ struct weapon_type;
typedef enum {
seen_none,
seen_neighbour,
+ seen_lighthouse_land,
seen_lighthouse,
seen_travel,
seen_unit,
diff --git a/src/kernel/unit.c b/src/kernel/unit.c
index 23bcc9982..2a9a3b5da 100644
--- a/src/kernel/unit.c
+++ b/src/kernel/unit.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/laws.c b/src/laws.c
index e8200c262..36d914cde 100644
--- a/src/laws.c
+++ b/src/laws.c
@@ -1,6 +1,5 @@
/*
-Copyright (c) 1998-2014,
-Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
@@ -36,6 +35,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "monsters.h"
#include "move.h"
#include "randenc.h"
+#include "recruit.h"
#include "renumber.h"
#include "spy.h"
#include "study.h"
@@ -281,14 +281,13 @@ static void live(region * r)
#define MAX_EMIGRATION(p) ((p)/MAXDIRECTIONS)
#define MAX_IMMIGRATION(p) ((p)*2/3)
-static void calculate_emigration(region * r)
+void peasant_migration(region * r)
{
int i;
int maxp = region_maxworkers(r);
int rp = rpeasants(r);
int max_immigrants = MAX_IMMIGRATION(maxp - rp);
-
if (volcano_module()) {
static int terrain_cache;
static const terrain_type *t_volcano;
@@ -314,8 +313,14 @@ static void calculate_emigration(region * r)
if (max_emigration > 0) {
if (max_emigration > max_immigrants) max_emigration = max_immigrants;
- r->land->newpeasants += max_emigration;
- rc->land->newpeasants -= max_emigration;
+ if (max_emigration + r->land->newpeasants > USHRT_MAX) {
+ max_emigration = USHRT_MAX - r->land->newpeasants;
+ }
+ if (max_emigration + rc->land->newpeasants > USHRT_MAX) {
+ max_emigration = USHRT_MAX - rc->land->newpeasants;
+ }
+ r->land->newpeasants += (short)max_emigration;
+ rc->land->newpeasants -= (short)max_emigration;
max_immigrants -= max_emigration;
}
}
@@ -779,6 +784,7 @@ void immigration(void)
/* FIXME: kann ernsthaft abs(newpeasants) > rpeasants(r) sein? */
if (rp < 0) rp = 0;
rsetpeasants(r, rp);
+ r->land->newpeasants = 0;
}
/* Genereate some (0-6 depending on the income) peasants out of nothing */
/* if less than 50 are in the region and there is space and no monster or demon units in the region */
@@ -878,7 +884,7 @@ void demographics(void)
/* Seuchen erst nachdem die Bauern sich vermehrt haben
* und gewandert sind */
- calculate_emigration(r);
+ peasant_migration(r);
peasants(r, peasant_rules);
if (r->age > 20) {
@@ -947,6 +953,8 @@ void transfer_faction(faction *fsrc, faction *fdst) {
int skill_count[MAXSKILLS];
int skill_limit[MAXSKILLS];
+ assert(fsrc != fdst);
+
for (sk = 0; sk != MAXSKILLS; ++sk) {
skill_limit[sk] = faction_skill_limit(fdst, sk);
}
@@ -963,7 +971,10 @@ void transfer_faction(faction *fsrc, faction *fdst) {
}
}
- for (u = fsrc->units; u != NULL; u = u->nextF) {
+ u = fsrc->units;
+ while (u) {
+ unit *unext = u->nextF;
+
if (u_race(u) == fdst->race) {
u->flags &= ~UFL_HERO;
if (give_unit_allowed(u) == 0) {
@@ -978,12 +989,15 @@ void transfer_faction(faction *fsrc, faction *fdst) {
}
}
if (i != u->skill_size) {
+ u = u->nextF;
continue;
}
}
+ ADDMSG(&fdst->msgs, msg_message("transfer_unit", "unit", u));
u_setfaction(u, fdst);
}
}
+ u = unext;
}
}
@@ -1003,6 +1017,7 @@ int quit_cmd(unit * u, struct order *ord)
param_t p;
p = getparam(f->locale);
if (p == P_FACTION) {
+#ifdef QUIT_WITH_TRANSFER
faction *f2 = getfaction();
if (f2 == NULL) {
cmistake(u, ord, 66, MSG_EVENT);
@@ -1015,17 +1030,23 @@ int quit_cmd(unit * u, struct order *ord)
else {
unit *u2;
for (u2 = u->region->units; u2; u2 = u2->next) {
- if (u2->faction == f2 && ucontact(u2, u)) {
- transfer_faction(u->faction, u2->faction);
- break;
+ if (u2->faction == f2) {
+ if (ucontact(u2, u)) {
+ transfer_faction(u->faction, u2->faction);
+ break;
+ }
}
}
if (u2 == NULL) {
/* no target unit found */
- cmistake(u, ord, 0, MSG_EVENT);
+ cmistake(u, ord, 40, MSG_EVENT);
flags = 0;
}
}
+#else
+ log_error("faction %s: QUIT FACTION is disabled.", factionname(f));
+ flags = 0;
+#endif
}
}
f->flags |= flags;
@@ -2152,8 +2173,13 @@ int banner_cmd(unit * u, struct order *ord)
init_order_depr(ord);
s = getstrtoken();
- faction_setbanner(u->faction, s);
- ADDMSG(&u->faction->msgs, msg_message("changebanner", "value", s));
+ if (!s || !s[0]) {
+ cmistake(u, ord, 125, MSG_EVENT);
+ }
+ else {
+ faction_setbanner(u->faction, s);
+ ADDMSG(&u->faction->msgs, msg_message("changebanner", "value", s));
+ }
return 0;
}
@@ -3872,7 +3898,6 @@ void init_processor(void)
add_proc_order(p, K_GROUP, group_cmd, 0, NULL);
p += 10;
- add_proc_order(p, K_QUIT, quit_cmd, 0, NULL);
add_proc_order(p, K_URSPRUNG, origin_cmd, 0, NULL);
add_proc_order(p, K_ALLY, ally_cmd, 0, NULL);
add_proc_order(p, K_PREFIX, prefix_cmd, 0, NULL);
@@ -3896,6 +3921,7 @@ void init_processor(void)
p += 10; /* all claims must be done before we can USE */
add_proc_region(p, enter_1, "Betreten (1. Versuch)"); /* for GIVE CONTROL */
add_proc_order(p, K_USE, use_cmd, 0, "Benutzen");
+ add_proc_order(p, K_QUIT, quit_cmd, 0, "Stirb");
p += 10; /* in case it has any effects on alliance victories */
add_proc_order(p, K_GIVE, give_control_cmd, 0, "GIB KOMMANDO");
@@ -3923,11 +3949,14 @@ void init_processor(void)
if (rule_force_leave(FORCE_LEAVE_ALL)) {
add_proc_region(p, do_force_leave, "kick non-allies out of buildings/ships");
}
- add_proc_region(p, economics, "Zerstoeren, Geben, Rekrutieren, Vergessen");
+ add_proc_region(p, economics, "Geben, Vergessen");
+ add_proc_region(p+1, recruit, "Rekrutieren");
+ add_proc_region(p+2, destroy, "Zerstoeren");
/* all recruitment must be finished before we can calculate
* promotion cost of ability */
p += 10;
+ add_proc_global(p, quit, "Sterben");
add_proc_order(p, K_PROMOTION, promotion_cmd, 0, "Heldenbefoerderung");
p += 10;
@@ -3936,9 +3965,6 @@ void init_processor(void)
}
add_proc_postregion(p, maintain_buildings, "Gebaeudeunterhalt");
- p += 10; /* QUIT fuer sich alleine */
- add_proc_global(p, quit, "Sterben");
-
if (!keyword_disabled(K_CAST)) {
p += 10;
add_proc_global(p, magic, "Zaubern");
diff --git a/src/laws.h b/src/laws.h
index 1f9632c60..fe423302e 100755
--- a/src/laws.h
+++ b/src/laws.h
@@ -93,6 +93,8 @@ extern "C" {
int reserve_cmd(struct unit *u, struct order *ord);
int reserve_self(struct unit *u, struct order *ord);
int claim_cmd(struct unit *u, struct order *ord);
+ void transfer_faction(struct faction *fsrc, struct faction *fdst);
+ void peasant_migration(struct region * r);
void nmr_warnings(void);
bool nmr_death(const struct faction * f, int turn, int timeout);
@@ -119,6 +121,8 @@ extern "C" {
enum param_t findparam_ex(const char *s, const struct locale * lang);
+#define QUIT_WITH_TRANSFER
+
#ifdef __cplusplus
}
#endif
diff --git a/src/laws.test.c b/src/laws.test.c
index 33742f427..795656706 100644
--- a/src/laws.test.c
+++ b/src/laws.test.c
@@ -1248,6 +1248,68 @@ static void test_ally_cmd_errors(CuTest *tc) {
test_teardown();
}
+static void test_banner_cmd(CuTest *tc) {
+ unit *u;
+ faction *f;
+ order *ord;
+
+ test_setup();
+ mt_create_error(125);
+ mt_create_va(mt_new("changebanner", NULL), "value:string", MT_NEW_END);
+ u = test_create_unit(f = test_create_faction(NULL), test_create_region(0, 0, NULL));
+
+ ord = create_order(K_BANNER, f->locale, "Hodor!");
+ banner_cmd(u, ord);
+ CuAssertStrEquals(tc, "Hodor!", faction_getbanner(f));
+ CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "changebanner"));
+ free_order(ord);
+ test_clear_messages(f);
+
+ ord = create_order(K_BANNER, f->locale, NULL);
+ banner_cmd(u, ord);
+ CuAssertStrEquals(tc, "Hodor!", faction_getbanner(f));
+ CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error125"));
+ free_order(ord);
+ test_clear_messages(f);
+
+ test_teardown();
+}
+
+static void test_email_cmd(CuTest *tc) {
+ unit *u;
+ faction *f;
+ order *ord;
+
+ test_setup();
+ mt_create_error(85);
+ mt_create_va(mt_new("changemail", NULL), "value:string", MT_NEW_END);
+ mt_create_va(mt_new("changemail_invalid", NULL), "value:string", MT_NEW_END);
+ u = test_create_unit(f = test_create_faction(NULL), test_create_region(0, 0, NULL));
+
+ ord = create_order(K_EMAIL, f->locale, "hodor@example.com");
+ email_cmd(u, ord);
+ CuAssertStrEquals(tc, "hodor@example.com", f->email);
+ CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "changemail"));
+ free_order(ord);
+ test_clear_messages(f);
+
+ ord = create_order(K_EMAIL, f->locale, "example.com");
+ email_cmd(u, ord);
+ CuAssertStrEquals(tc, "hodor@example.com", f->email);
+ CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "changemail_invalid"));
+ free_order(ord);
+ test_clear_messages(f);
+
+ ord = create_order(K_EMAIL, f->locale, NULL);
+ email_cmd(u, ord);
+ CuAssertStrEquals(tc, "hodor@example.com", f->email);
+ CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error85"));
+ free_order(ord);
+ test_clear_messages(f);
+
+ test_teardown();
+}
+
static void test_name_cmd(CuTest *tc) {
unit *u;
faction *f;
@@ -1869,6 +1931,53 @@ static void test_long_order_on_ocean(CuTest *tc) {
test_teardown();
}
+static void test_peasant_migration(CuTest *tc) {
+ region *r1, *r2;
+ int rmax;
+
+ test_setup();
+ config_set("rules.economy.repopulate_maximum", "0");
+ r1 = test_create_plain(0, 0);
+ rsettrees(r1, 0, 0);
+ rsettrees(r1, 1, 0);
+ rsettrees(r1, 2, 0);
+ rmax = region_maxworkers(r1);
+ r2 = test_create_plain(0, 1);
+ rsettrees(r2, 0, 0);
+ rsettrees(r2, 1, 0);
+ rsettrees(r2, 2, 0);
+
+ rsetpeasants(r1, rmax - 90);
+ rsetpeasants(r2, rmax);
+ peasant_migration(r1);
+ immigration();
+ CuAssertIntEquals(tc, rmax - 90, rpeasants(r1));
+ CuAssertIntEquals(tc, rmax, rpeasants(r2));
+
+ rsetpeasants(r1, rmax - 90);
+ rsetpeasants(r2, rmax + 60);
+ peasant_migration(r1);
+ immigration();
+ CuAssertIntEquals(tc, rmax - 80, rpeasants(r1));
+ CuAssertIntEquals(tc, rmax + 50, rpeasants(r2));
+
+ rsetpeasants(r1, rmax - 6); /* max 4 immigrants. */
+ rsetpeasants(r2, rmax + 60); /* max 10 emigrants. */
+ peasant_migration(r1);
+ immigration(); /* 4 peasants will move */
+ CuAssertIntEquals(tc, rmax - 2, rpeasants(r1));
+ CuAssertIntEquals(tc, rmax + 56, rpeasants(r2));
+
+ rsetpeasants(r1, rmax - 6); /* max 4 immigrants. */
+ rsetpeasants(r2, rmax + 6); /* max 1 emigrant. */
+ peasant_migration(r1);
+ immigration(); /* 4 peasants will move */
+ CuAssertIntEquals(tc, rmax - 5, rpeasants(r1));
+ CuAssertIntEquals(tc, rmax + 5, rpeasants(r2));
+
+ test_teardown();
+}
+
static void test_quit(CuTest *tc) {
faction *f;
unit *u;
@@ -1891,6 +2000,7 @@ static void test_quit(CuTest *tc) {
test_teardown();
}
+#ifdef QUIT_WITH_TRANSFER
/**
* Gifting units to another faction upon voluntary death (QUIT).
*/
@@ -2012,6 +2122,30 @@ static void test_quit_transfer_hero(CuTest *tc) {
test_teardown();
}
+static void test_transfer_faction(CuTest *tc) {
+ faction *f1, *f2;
+ unit *u1, *u2, *u3, *u4;
+ region *r;
+
+ test_setup();
+ r = test_create_plain(0, 0);
+ f1 = test_create_faction(NULL);
+ f2 = test_create_faction(NULL);
+ u1 = test_create_unit(f1, r);
+ u2 = test_create_unit(f1, r);
+ u_setrace(u2, test_create_race("smurf"));
+ u3 = test_create_unit(f2, r);
+ u4 = test_create_unit(f1, r);
+ transfer_faction(f1, f2);
+ CuAssertPtrEquals(tc, f2, u1->faction);
+ CuAssertPtrEquals(tc, f1, u2->faction);
+ CuAssertPtrEquals(tc, f2, u3->faction);
+ CuAssertPtrEquals(tc, f2, u4->faction);
+
+ test_teardown();
+}
+#endif
+
CuSuite *get_laws_suite(void)
{
CuSuite *suite = CuSuiteNew();
@@ -2021,6 +2155,8 @@ CuSuite *get_laws_suite(void)
SUITE_ADD_TEST(suite, test_nmr_warnings);
SUITE_ADD_TEST(suite, test_ally_cmd);
SUITE_ADD_TEST(suite, test_name_cmd);
+ SUITE_ADD_TEST(suite, test_banner_cmd);
+ SUITE_ADD_TEST(suite, test_email_cmd);
SUITE_ADD_TEST(suite, test_name_cmd_2274);
SUITE_ADD_TEST(suite, test_name_unit);
SUITE_ADD_TEST(suite, test_name_region);
@@ -2088,10 +2224,14 @@ CuSuite *get_laws_suite(void)
SUITE_ADD_TEST(suite, test_long_orders);
SUITE_ADD_TEST(suite, test_long_order_on_ocean);
SUITE_ADD_TEST(suite, test_quit);
+ SUITE_ADD_TEST(suite, test_peasant_migration);
+#ifdef QUIT_WITH_TRANSFER
SUITE_ADD_TEST(suite, test_quit_transfer);
SUITE_ADD_TEST(suite, test_quit_transfer_limited);
SUITE_ADD_TEST(suite, test_quit_transfer_migrants);
SUITE_ADD_TEST(suite, test_quit_transfer_hero);
+ SUITE_ADD_TEST(suite, test_transfer_faction);
+#endif
return suite;
}
diff --git a/src/magic.c b/src/magic.c
index 5b64b1459..ee522985c 100644
--- a/src/magic.c
+++ b/src/magic.c
@@ -1,6 +1,5 @@
/*
-Copyright (c) 1998-2014,
-Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/main.c b/src/main.c
index 4216d3046..452b92353 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2018, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
@@ -193,7 +193,7 @@ static int parse_args(int argc, char **argv)
else if (argi[1] == '-') { /* long format */
if (strcmp(argi + 2, "version") == 0) {
printf("Eressea version %s, "
- "Copyright (C) 2018 Enno Rehling et al.\n",
+ "Copyright (C) 2019 Enno Rehling et al.\n",
eressea_version());
return 1;
#ifdef USE_CURSES
diff --git a/src/modules/autoseed.c b/src/modules/autoseed.c
index a59567dd4..7a6420e1c 100644
--- a/src/modules/autoseed.c
+++ b/src/modules/autoseed.c
@@ -565,8 +565,8 @@ int autoseed(newfaction ** players, int nsize, int max_agediff)
nfp = &nextf->next;
while (*nfp) {
newfaction *nf = *nfp;
- if (strcmp(nextf->email, nf->email) == 0) {
- log_warning("Duplicate email %s\n", nf->email?nf->email:"");
+ if (nf->email && nextf->email && strcmp(nextf->email, nf->email) == 0) {
+ log_warning("Duplicate email %s\n", nf->email ? nf->email : "");
*nfp = nf->next;
free_newfaction(nf);
}
diff --git a/src/modules/museum.c b/src/modules/museum.c
index 36aa1d63f..72e0b31b5 100644
--- a/src/modules/museum.c
+++ b/src/modules/museum.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/modules/score.c b/src/modules/score.c
index 0ffb73301..31a06d1cd 100644
--- a/src/modules/score.c
+++ b/src/modules/score.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/monsters.c b/src/monsters.c
index 535e03166..24b3ec397 100644
--- a/src/monsters.c
+++ b/src/monsters.c
@@ -1,5 +1,5 @@
/*
- * Eressea PB(E)M host Copyright (C) 1998-2015
+ * Eressea PB(E)M host Copyright (C) 1998-2019
* Christian Schlittchen (corwin@amber.kn-bremen.de)
* Katja Zedel (katze@felidae.kn-bremen.de)
* Henning Peters (faroul@beyond.kn-bremen.de)
@@ -730,6 +730,17 @@ static order *plan_dragon(unit * u)
return long_order;
}
+static void monster_cannibalism(unit *u) {
+ unit *u2;
+
+ for (u2 = u->next; u2; u2 = u2->next) {
+ if (u2->_race == u->_race) {
+ stats_count("monsters.cannibalism", u2->number);
+ u2->number = 0;
+ }
+ }
+}
+
void plan_monsters(faction * f)
{
region *r;
@@ -748,14 +759,20 @@ void plan_monsters(faction * f)
bool can_move = true;
/* Ab hier nur noch Befehle fuer NPC-Einheiten. */
- if (u->faction!=f)
+ if (u->faction != f || u->number <= 0) {
continue;
+ }
/* Parteitarnung von Monstern ist doof: */
if (fval(u, UFL_ANON_FACTION)) {
u->flags &= ~UFL_ANON_FACTION;
}
+ if (rc->splitsize < 10) {
+ /* hermit-type monsters eat each other */
+ monster_cannibalism(u);
+ }
+
if (skill_enabled(SK_PERCEPTION)) {
/* Monster bekommen jede Runde ein paar Tage Wahrnehmung dazu */
produceexp(u, SK_PERCEPTION, u->number);
diff --git a/src/morale.c b/src/morale.c
index 7d76db6b9..5fd2c8205 100644
--- a/src/morale.c
+++ b/src/morale.c
@@ -1,6 +1,5 @@
/*
-Copyright (c) 1998-2014,
-Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/move.c b/src/move.c
index baddba11e..984904608 100644
--- a/src/move.c
+++ b/src/move.c
@@ -1,6 +1,5 @@
/*
-Copyright (c) 1998-2018,
-Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/names.c b/src/names.c
index bf753d8db..c1193d5f5 100644
--- a/src/names.c
+++ b/src/names.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/races/dragons.c b/src/races/dragons.c
index 872893b64..c5fabd09f 100644
--- a/src/races/dragons.c
+++ b/src/races/dragons.c
@@ -1,7 +1,5 @@
/*
- *
- *
- * Eressea PB(E)M host Copyright (C) 1998-2015
+ * Eressea PB(E)M host Copyright (C) 1998-2019
* Christian Schlittchen (corwin@amber.kn-bremen.de)
* Katja Zedel (katze@felidae.kn-bremen.de)
* Henning Peters (faroul@beyond.kn-bremen.de)
diff --git a/src/races/zombies.c b/src/races/zombies.c
index 652ccc1e2..3227f621a 100644
--- a/src/races/zombies.c
+++ b/src/races/zombies.c
@@ -1,5 +1,5 @@
/*
- * Eressea PB(E)M host Copyright (C) 1998-2015
+ * Eressea PB(E)M host Copyright (C) 1998-2019
* Christian Schlittchen (corwin@amber.kn-bremen.de)
* Katja Zedel (katze@felidae.kn-bremen.de)
* Henning Peters (faroul@beyond.kn-bremen.de)
diff --git a/src/randenc.c b/src/randenc.c
index e6ebf549b..0011e5b0e 100644
--- a/src/randenc.c
+++ b/src/randenc.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/recruit.c b/src/recruit.c
new file mode 100644
index 000000000..541202f08
--- /dev/null
+++ b/src/recruit.c
@@ -0,0 +1,506 @@
+/*
+Copyright (c) 1998-2019,
+Enno Rehling
+Katja Zedel
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+**/
+
+#ifdef _MSC_VER
+#include
+#endif
+
+#include "recruit.h"
+
+#include "alchemy.h"
+#include "direction.h"
+#include "donations.h"
+#include "guard.h"
+#include "give.h"
+#include "laws.h"
+#include "randenc.h"
+#include "spy.h"
+#include "study.h"
+#include "move.h"
+#include "monsters.h"
+#include "morale.h"
+#include "reports.h"
+
+#include
+#include
+#include
+#include
+#include
+
+/* kernel includes */
+#include "kernel/ally.h"
+#include "kernel/attrib.h"
+#include "kernel/building.h"
+#include "kernel/calendar.h"
+#include "kernel/config.h"
+#include "kernel/curse.h"
+#include "kernel/equipment.h"
+#include "kernel/event.h"
+#include "kernel/faction.h"
+#include "kernel/item.h"
+#include "kernel/messages.h"
+#include "kernel/order.h"
+#include "kernel/plane.h"
+#include "kernel/pool.h"
+#include "kernel/race.h"
+#include "kernel/region.h"
+#include "kernel/resources.h"
+#include "kernel/ship.h"
+#include "kernel/terrain.h"
+#include "kernel/terrainid.h"
+#include "kernel/unit.h"
+
+/* util includes */
+#include
+#include
+#include
+#include
+#include
+#include "util/param.h"
+#include
+#include
+
+/* libs includes */
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define RECRUIT_MERGE 1
+static int rules_recruit = -1;
+
+typedef struct recruit_request {
+ struct recruit_request *next;
+ struct unit *unit;
+ struct order *ord;
+ int qty;
+} recruit_request;
+
+typedef struct recruitment {
+ struct recruitment *next;
+ faction *f;
+ recruit_request *requests;
+ int total, assigned;
+} recruitment;
+
+static void recruit_init(void)
+{
+ if (rules_recruit < 0) {
+ rules_recruit = 0;
+ if (config_get_int("recruit.allow_merge", 1)) {
+ rules_recruit |= RECRUIT_MERGE;
+ }
+ }
+}
+
+static void free_requests(recruit_request *requests) {
+ while (requests) {
+ recruit_request *req = requests->next;
+ free(requests);
+ requests = req;
+ }
+}
+
+void free_recruitments(recruitment * recruits)
+{
+ while (recruits) {
+ recruitment *rec = recruits;
+ recruits = rec->next;
+ free_requests(rec->requests);
+ free(rec);
+ }
+}
+
+/** Creates a list of recruitment structs, one for each faction. Adds every quantifyable production
+ * to the faction's struct and to total.
+ */
+static recruitment *select_recruitment(recruit_request ** rop,
+ int(*quantify) (const struct race *, int), int *total)
+{
+ recruitment *recruits = NULL;
+
+ while (*rop) {
+ recruitment *rec = recruits;
+ recruit_request *ro = *rop;
+ unit *u = ro->unit;
+ const race *rc = u_race(u);
+ int qty = quantify(rc, ro->qty);
+
+ if (qty < 0) {
+ rop = &ro->next; /* skip this one */
+ }
+ else {
+ *rop = ro->next; /* remove this one */
+ while (rec && rec->f != u->faction)
+ rec = rec->next;
+ if (rec == NULL) {
+ rec = (recruitment *)malloc(sizeof(recruitment));
+ if (!rec) abort();
+ rec->f = u->faction;
+ rec->total = 0;
+ rec->assigned = 0;
+ rec->requests = NULL;
+ rec->next = recruits;
+ recruits = rec;
+ }
+ *total += qty;
+ rec->total += qty;
+ ro->next = rec->requests;
+ rec->requests = ro;
+ }
+ }
+ return recruits;
+}
+
+void add_recruits(unit * u, int number, int wanted)
+{
+ region *r = u->region;
+ assert(number <= wanted);
+ if (number > 0) {
+ unit *unew;
+ char equipment[64];
+ int len;
+
+ if (u->number == 0) {
+ set_number(u, number);
+ u->hp = number * unit_max_hp(u);
+ unew = u;
+ }
+ else {
+ unew = create_unit(r, u->faction, number, u_race(u), 0, NULL, u);
+ }
+
+ len = snprintf(equipment, sizeof(equipment), "new_%s", u_race(u)->_name);
+ if (len > 0 && (size_t)len < sizeof(equipment)) {
+ equip_unit(unew, equipment);
+ }
+ if (unew != u) {
+ transfermen(unew, u, unew->number);
+ remove_unit(&r->units, unew);
+ }
+ }
+ if (number < wanted) {
+ ADDMSG(&u->faction->msgs, msg_message("recruit",
+ "unit region amount want", u, r, number, wanted));
+ }
+}
+
+static int any_recruiters(const struct race *rc, int qty)
+{
+ return (int)(qty * 2 * rc->recruit_multi);
+}
+
+static int do_recruiting(recruitment * recruits, int available)
+{
+ recruitment *rec;
+ int recruited = 0;
+
+ /* try to assign recruits to factions fairly */
+ while (available > 0) {
+ int n = 0;
+ int rest, mintotal = INT_MAX;
+
+ /* find smallest production */
+ for (rec = recruits; rec != NULL; rec = rec->next) {
+ int want = rec->total - rec->assigned;
+ if (want > 0) {
+ if (mintotal > want)
+ mintotal = want;
+ ++n;
+ }
+ }
+ if (n == 0)
+ break;
+ if (mintotal * n > available) {
+ mintotal = available / n;
+ }
+ rest = available - mintotal * n;
+
+ /* assign size of smallest production for everyone if possible; in the end roll dice to assign
+ * small rest */
+ for (rec = recruits; rec != NULL; rec = rec->next) {
+ int want = rec->total - rec->assigned;
+
+ if (want > 0) {
+ int get = mintotal;
+ if (want > mintotal && rest < n && (rng_int() % n) < rest) {
+ --rest;
+ ++get;
+ }
+ assert(get <= want);
+ available -= get;
+ rec->assigned += get;
+ }
+ }
+ }
+
+ /* do actual recruiting */
+ for (rec = recruits; rec != NULL; rec = rec->next) {
+ recruit_request *req;
+ int get = rec->assigned;
+
+ for (req = rec->requests; req; req = req->next) {
+ unit *u = req->unit;
+ const race *rc = u_race(u); /* race is set in recruit() */
+ int number;
+ double multi = 2.0 * rc->recruit_multi;
+
+ number = (int)(get / multi);
+ if (number > req->qty) number = req->qty;
+ if (rc->recruitcost) {
+ int afford = get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT,
+ number * rc->recruitcost) / rc->recruitcost;
+ if (number > afford) number = afford;
+ }
+ if (u->number + number > UNIT_MAXSIZE) {
+ ADDMSG(&u->faction->msgs, msg_feedback(u, req->ord, "error_unit_size",
+ "maxsize", UNIT_MAXSIZE));
+ number = UNIT_MAXSIZE - u->number;
+ assert(number >= 0);
+ }
+ if (rc->recruitcost) {
+ use_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT,
+ rc->recruitcost * number);
+ }
+ if (u->number == 0 && fval(u, UFL_DEAD)) {
+ /* unit is empty, dead, and cannot recruit */
+ number = 0;
+ }
+ add_recruits(u, number, req->qty);
+ if (number > 0) {
+ int dec = (int)(number * multi);
+ if ((rc->ec_flags & ECF_REC_ETHEREAL) == 0) {
+ recruited += dec;
+ }
+
+ get -= dec;
+ }
+ }
+ }
+ return recruited;
+}
+
+/* Rekrutierung */
+static void expandrecruit(region * r, recruit_request * recruitorders)
+{
+ recruitment *recruits;
+ int orc_total = 0;
+
+ /* peasant limited: */
+ recruits = select_recruitment(&recruitorders, any_recruiters, &orc_total);
+ if (recruits) {
+ int orc_recruited, orc_peasants = rpeasants(r) * 2;
+ int orc_frac = orc_peasants / RECRUITFRACTION; /* anzahl orks. 2 ork = 1 bauer */
+ if (orc_total < orc_frac)
+ orc_frac = orc_total;
+ orc_recruited = do_recruiting(recruits, orc_frac);
+ assert(orc_recruited <= orc_frac);
+ rsetpeasants(r, (orc_peasants - orc_recruited) / 2);
+ free_recruitments(recruits);
+ }
+
+ /* no limit: */
+ recruits = select_recruitment(&recruitorders, any_recruiters, &orc_total);
+ if (recruits) {
+ int recruited, peasants = rpeasants(r) * 2;
+ recruited = do_recruiting(recruits, INT_MAX);
+ if (recruited > 0) {
+ rsetpeasants(r, (peasants - recruited) / 2);
+ }
+ free_recruitments(recruits);
+ }
+
+ assert(recruitorders == NULL);
+}
+
+static int recruit_cost(const faction * f, const race * rc)
+{
+ if (is_monsters(f) || valid_race(f, rc)) {
+ return rc->recruitcost;
+ }
+ return -1;
+}
+
+message *can_recruit(unit *u, const race *rc, order *ord, int now)
+{
+ region *r = u->region;
+
+ /* this is a very special case because the recruiting unit may be empty
+ * at this point and we have to look at the creating unit instead. This
+ * is done in cansee, which is called indirectly by is_guarded(). */
+ if (is_guarded(r, u)) {
+ return msg_error(u, ord, 70);
+ }
+
+ if (rc == get_race(RC_INSECT)) {
+ gamedate date;
+ get_gamedate(now, &date);
+ if (date.season == SEASON_WINTER && r->terrain != newterrain(T_DESERT)) {
+ bool usepotion = false;
+ unit *u2;
+
+ for (u2 = r->units; u2; u2 = u2->next) {
+ if (fval(u2, UFL_WARMTH)) {
+ usepotion = true;
+ break;
+ }
+ }
+ if (!usepotion) {
+ return msg_error(u, ord, 98);
+ }
+ }
+ /* in Gletschern, Eisbergen gar nicht rekrutieren */
+ if (r_insectstalled(r)) {
+ return msg_error(u, ord, 97);
+ }
+ }
+ if (is_cursed(r->attribs, &ct_riotzone)) {
+ /* Die Region befindet sich in Aufruhr */
+ return msg_error(u, ord, 237);
+ }
+
+ if (rc && !playerrace(rc)) {
+ return msg_error(u, ord, 139);
+ }
+
+ if (fval(u, UFL_HERO)) {
+ return msg_feedback(u, ord, "error_herorecruit", "");
+ }
+ if (has_skill(u, SK_MAGIC)) {
+ /* error158;de;{unit} in {region}: '{command}' - Magier arbeiten
+ * grundsaetzlich nur alleine! */
+ return msg_error(u, ord, 158);
+ }
+ return NULL;
+}
+
+static void recruit_cmd(unit * u, struct order *ord, recruit_request ** recruitorders)
+{
+ region *r = u->region;
+ recruit_request *o;
+ int recruitcost = -1;
+ const faction *f = u->faction;
+ const struct race *rc = u_race(u);
+ int n;
+ message *msg;
+
+ init_order_depr(ord);
+ n = getint();
+ if (n <= 0) {
+ syntax_error(u, ord);
+ return;
+ }
+
+ if (u->number == 0) {
+ char token[128];
+ const char *str;
+
+ str = gettoken(token, sizeof(token));
+ if (str && str[0]) {
+ /* Monsters can RECRUIT 15 DRACOID
+ * also: secondary race */
+ rc = findrace(str, f->locale);
+ if (rc != NULL) {
+ recruitcost = recruit_cost(f, rc);
+ }
+ }
+ }
+
+ if (recruitcost < 0) {
+ rc = u_race(u);
+ recruitcost = recruit_cost(f, rc);
+ if (recruitcost < 0) {
+ recruitcost = INT_MAX;
+ }
+ }
+
+ if (recruitcost > 0) {
+ int pool;
+ plane *pl = getplane(r);
+
+ if (pl && (pl->flags & PFL_NORECRUITS)) {
+ ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_pflnorecruit", ""));
+ return;
+ }
+
+ pool = get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT, recruitcost * n);
+ if (pool < recruitcost) {
+ cmistake(u, ord, 142, MSG_EVENT);
+ return;
+ }
+ pool /= recruitcost;
+ if (n > pool) n = pool;
+ }
+
+ if (!n) {
+ cmistake(u, ord, 142, MSG_EVENT);
+ return;
+ }
+ if (has_skill(u, SK_ALCHEMY)) {
+ if (faction_count_skill(u->faction, SK_ALCHEMY) + n > faction_skill_limit(u->faction, SK_ALCHEMY)) {
+ cmistake(u, ord, 156, MSG_EVENT);
+ return;
+ }
+ }
+
+ assert(rc);
+ msg = can_recruit(u, rc, ord, turn);
+ if (msg) {
+ add_message(&u->faction->msgs, msg);
+ msg_release(msg);
+ return;
+ }
+
+ u_setrace(u, rc);
+ u->wants = n;
+ o = (recruit_request *)calloc(1, sizeof(recruit_request));
+ if (!o) abort();
+ o->qty = n;
+ o->unit = u;
+ o->ord = ord;
+ addlist(recruitorders, o);
+}
+
+void recruit(region * r)
+{
+ unit *u;
+ recruit_request *recruitorders = NULL;
+
+ if (rules_recruit < 0)
+ recruit_init();
+ for (u = r->units; u; u = u->next) {
+ order *ord;
+
+ if ((rules_recruit & RECRUIT_MERGE) || u->number == 0) {
+ for (ord = u->orders; ord; ord = ord->next) {
+ if (getkeyword(ord) == K_RECRUIT) {
+ recruit_cmd(u, ord, &recruitorders);
+ break;
+ }
+ }
+ }
+ }
+
+ if (recruitorders) {
+ expandrecruit(r, recruitorders);
+ }
+ remove_empty_units_in_region(r);
+}
diff --git a/src/recruit.h b/src/recruit.h
new file mode 100644
index 000000000..347ea99f9
--- /dev/null
+++ b/src/recruit.h
@@ -0,0 +1,40 @@
+/*
+Copyright (c) 1998-2015, Enno Rehling
+Katja Zedel
+
+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.
+**/
+
+#pragma once
+#ifndef H_GC_RECRUIT
+#define H_GC_RECRUIT
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ struct message;
+ struct order;
+ struct race;
+ struct region;
+ struct unit;
+
+ struct message *can_recruit(struct unit *u, const struct race *rc, struct order *ord, int now);
+ void add_recruits(struct unit * u, int number, int wanted);
+ void recruit(struct region * r);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/report.c b/src/report.c
index 61cb915b8..a6e55409d 100644
--- a/src/report.c
+++ b/src/report.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
@@ -750,7 +750,7 @@ static void append_message(sbstring *sbp, message *m, const faction * f) {
sbp->end += size;
}
-static void prices(struct stream *out, const region * r, const faction * f)
+static void report_prices(struct stream *out, const region * r, const faction * f)
{
const luxury_type *sale = NULL;
struct demand *dmd;
@@ -771,6 +771,7 @@ static void prices(struct stream *out, const region * r, const faction * f)
m = msg_message("nr_market_sale", "product price",
sale->itype->rtype, sale->price);
+ newline(out);
nr_render(m, f->locale, buf, sizeof(buf), f);
msg_release(m);
sbs_adopt(&sbs, buf, sizeof(buf));
@@ -867,7 +868,8 @@ static void report_region_description(struct stream *out, const region * r, fact
sbs_strcat(&sbs, LOC(f->locale, "see_neighbour"));
sbs_strcat(&sbs, ")");
}
- else if (r->seen.mode == seen_lighthouse) {
+ else if ((r->seen.mode == seen_lighthouse)
+ || (r->seen.mode == seen_lighthouse_land)) {
sbs_strcat(&sbs, " (");
sbs_strcat(&sbs, LOC(f->locale, "see_lighthouse"));
sbs_strcat(&sbs, ")");
@@ -1154,17 +1156,22 @@ void report_region(struct stream *out, const region * r, faction * f)
}
report_region_description(out, r, f, see);
- report_region_schemes(out, r, f);
- report_region_edges(out, r, f, edges, ne);
+ if (r->seen.mode >= seen_unit) {
+ report_region_schemes(out, r, f);
+ }
+ if (r->seen.mode >= seen_lighthouse) {
+ report_region_edges(out, r, f, edges, ne);
+ }
}
-static void statistics(struct stream *out, const region * r, const faction * f)
+static void report_statistics(struct stream *out, const region * r, const faction * f)
{
int p = rpeasants(r);
message *m;
char buf[4096];
/* print */
+ newline(out);
m = msg_message("nr_stat_header", "region", r);
nr_render(m, f->locale, buf, sizeof(buf), f);
msg_release(m);
@@ -1592,6 +1599,8 @@ static void allies(struct stream *out, const faction * f)
const group *g = f->groups;
char prefix[64];
+ rpline(out);
+ newline(out);
centre(out, LOC(f->locale, "nr_alliances"), false);
newline(out);
@@ -1609,7 +1618,7 @@ static void allies(struct stream *out, const faction * f)
}
}
-static void guards(struct stream *out, const region * r, const faction * see)
+static void report_guards(struct stream *out, const region * r, const faction * see)
{
/* die Partei see sieht dies; wegen
* "unbekannte Partei", wenn man es selbst ist... */
@@ -1678,6 +1687,8 @@ static void list_address(struct stream *out, const faction * uf, selist * seenfa
int qi = 0;
selist *flist = seenfactions;
+ rpline(out);
+ newline(out);
centre(out, LOC(uf->locale, "nr_addresses"), false);
newline(out);
@@ -1806,12 +1817,10 @@ nr_building(struct stream *out, const region *r, const building *b, const factio
}
paragraph(out, buffer, 2, 0, 0);
- if (r->seen.mode >= seen_lighthouse) {
- nr_curses(out, 4, f, TYP_BUILDING, b);
- }
+ nr_curses(out, 4, f, TYP_BUILDING, b);
}
-static void nr_paragraph(struct stream *out, message * m, faction * f)
+static void nr_paragraph(struct stream *out, message * m, const faction * f)
{
char buf[4096];
@@ -1897,26 +1906,44 @@ static void cb_write_travelthru(region *r, unit *u, void *cbdata) {
void report_travelthru(struct stream *out, region *r, const faction *f)
{
- int maxtravel;
-
assert(r);
assert(f);
- if (!fval(r, RF_TRAVELUNIT)) {
- return;
+ if (fval(r, RF_TRAVELUNIT)) {
+ int maxtravel = count_travelthru(r, f);
+
+ if (maxtravel > 0) {
+ cb_data cbdata;
+ char buf[8192];
+
+ newline(out);
+ init_cb(&cbdata, out, buf, sizeof(buf), f);
+ cbdata.maxtravel = maxtravel;
+ cbdata.writep +=
+ str_strlcpy(buf, LOC(f->locale, "travelthru_header"), sizeof(buf));
+ travelthru_map(r, cb_write_travelthru, &cbdata);
+ return;
+ }
}
+}
- /* How many are we listing? For grammar. */
- maxtravel = count_travelthru(r, f);
- if (maxtravel > 0) {
- cb_data cbdata;
- char buf[8192];
+static void report_market(stream * out, const region *r, const faction *f) {
+ const item_type *lux = r_luxury(r);
+ const item_type *herb = r->land->herbtype;
+ message * m = NULL;
- init_cb(&cbdata, out, buf, sizeof(buf), f);
- cbdata.maxtravel = maxtravel;
- cbdata.writep +=
- str_strlcpy(buf, LOC(f->locale, "travelthru_header"), sizeof(buf));
- travelthru_map(r, cb_write_travelthru, &cbdata);
- return;
+ if (herb && lux) {
+ m = msg_message("nr_market_info_p", "p1 p2",
+ lux->rtype, herb->rtype);
+ }
+ else if (lux) {
+ m = msg_message("nr_market_info_s", "p1", lux->rtype);
+ }
+ else if (herb) {
+ m = msg_message("nr_market_info_s", "p1", herb->rtype);
+ }
+ if (m) {
+ newline(out);
+ nr_paragraph(out, m, f);
}
}
@@ -2120,121 +2147,92 @@ report_plaintext(const char *filename, report_context * ctx,
for (r = ctx->first; r != ctx->last; r = r->next) {
int stealthmod = stealth_modifier(r, f, r->seen.mode);
- building *b = r->buildings;
ship *sh = r->ships;
- if (r->seen.mode < seen_lighthouse)
- continue;
- /* Beschreibung */
+ if (r->seen.mode >= seen_lighthouse_land) {
+ rpline(out);
+ newline(out);
+ report_region(out, r, f);
+ }
- rpline(out);
- newline(out);
if (r->seen.mode >= seen_unit) {
anyunits = 1;
- report_region(out, r, f);
if (markets_module() && r->land) {
- const item_type *lux = r_luxury(r);
- const item_type *herb = r->land->herbtype;
-
- m = NULL;
- if (herb && lux) {
- m = msg_message("nr_market_info_p", "p1 p2",
- lux->rtype, herb->rtype);
- }
- else if (lux) {
- m = msg_message("nr_market_info_s", "p1",lux->rtype);
- }
- else if (herb) {
- m = msg_message("nr_market_info_s", "p1", herb->rtype);
- }
- if (m) {
- newline(out);
- nr_paragraph(out, m, f);
- }
+ report_market(out, r, f);
}
- else {
- if (!fval(r->terrain, SEA_REGION) && rpeasants(r) / TRADE_FRACTION > 0) {
- newline(out);
- prices(out, r, f);
- }
+ else if (!fval(r->terrain, SEA_REGION) && rpeasants(r) / TRADE_FRACTION > 0) {
+ report_prices(out, r, f);
}
- guards(out, r, f);
- newline(out);
+ report_guards(out, r, f);
report_travelthru(out, r, f);
- }
- else {
- report_region(out, r, f);
- newline(out);
- report_travelthru(out, r, f);
- }
-
- if (wants_stats && r->seen.mode >= seen_travel) {
- if (r->land || r->seen.mode >= seen_unit) {
- newline(out);
- statistics(out, r, f);
+ if (wants_stats) {
+ report_statistics(out, r, f);
}
}
+ else if (r->seen.mode >= seen_lighthouse) {
+ report_travelthru(out, r, f);
+ }
/* Nachrichten an REGION in der Region */
- if (r->seen.mode >= seen_travel) {
+ if (r->seen.mode >= seen_lighthouse) {
message_list *mlist = r_getmessages(r, f);
- newline(out);
if (mlist) {
struct mlist **split = merge_messages(mlist, r->msgs);
newline(out);
rp_messages(out, mlist, f, 0, false);
split_messages(mlist, split);
}
- else {
+ else if (r->msgs) {
+ newline(out);
rp_messages(out, r->msgs, f, 0, false);
}
- }
- /* report all units. they are pre-sorted in an efficient manner */
- u = r->units;
- while (b) {
- while (b && (!u || u->building != b)) {
- nr_building(out, r, b, f);
- b = b->next;
- }
- if (b) {
- nr_building(out, r, b, f);
- while (u && u->building == b) {
- if (visible_unit(u, f, stealthmod, r->seen.mode)) {
- nr_unit(out, f, u, 6, r->seen.mode);
+ /* report all units. they are pre-sorted in an efficient manner */
+ u = r->units;
+ if (r->seen.mode >= seen_travel) {
+ building *b = r->buildings;
+ while (b) {
+ while (b && (!u || u->building != b)) {
+ nr_building(out, r, b, f);
+ b = b->next;
}
- u = u->next;
- }
- b = b->next;
- }
- }
- while (u && !u->ship) {
- if (visible_unit(u, f, stealthmod, r->seen.mode)) {
- nr_unit(out, f, u, 4, r->seen.mode);
- }
- assert(!u->building);
- u = u->next;
- }
- while (sh) {
- while (sh && (!u || u->ship != sh)) {
- nr_ship(out, r, sh, f, NULL);
- sh = sh->next;
- }
- if (sh) {
- nr_ship(out, r, sh, f, u);
- while (u && u->ship == sh) {
- if (visible_unit(u, f, stealthmod, r->seen.mode)) {
- nr_unit(out, f, u, 6, r->seen.mode);
+ if (b) {
+ nr_building(out, r, b, f);
+ while (u && u->building == b) {
+ if (visible_unit(u, f, stealthmod, r->seen.mode)) {
+ nr_unit(out, f, u, 6, r->seen.mode);
+ }
+ u = u->next;
+ }
+ b = b->next;
}
- u = u->next;
}
- sh = sh->next;
}
+ while (u && !u->ship) {
+ if (visible_unit(u, f, stealthmod, r->seen.mode)) {
+ nr_unit(out, f, u, 4, r->seen.mode);
+ }
+ assert(!u->building);
+ u = u->next;
+ }
+ while (sh) {
+ while (sh && (!u || u->ship != sh)) {
+ nr_ship(out, r, sh, f, NULL);
+ sh = sh->next;
+ }
+ if (sh) {
+ nr_ship(out, r, sh, f, u);
+ while (u && u->ship == sh) {
+ if (visible_unit(u, f, stealthmod, r->seen.mode)) {
+ nr_unit(out, f, u, 6, r->seen.mode);
+ }
+ u = u->next;
+ }
+ sh = sh->next;
+ }
+ }
+ assert(!u);
}
-
- assert(!u);
-
- newline(out);
ERRNO_CHECK();
}
if (!is_monsters(f)) {
diff --git a/src/report.test.c b/src/report.test.c
index b6696ea1b..030850906 100644
--- a/src/report.test.c
+++ b/src/report.test.c
@@ -260,7 +260,7 @@ static void test_report_travelthru(CuTest *tc) {
out.api->rewind(out.handle);
len = out.api->read(out.handle, buf, sizeof(buf));
buf[len] = '\0';
- CuAssertStrEquals_Msg(tc, "list one unit", "Durchreise: Hodor (1).\n", buf);
+ CuAssertStrEquals_Msg(tc, "list one unit", "\nDurchreise: Hodor (1).\n", buf);
mstream_done(&out);
mstream_init(&out);
diff --git a/src/reports.c b/src/reports.c
index 306fe0e10..aa49c4653 100644
--- a/src/reports.c
+++ b/src/reports.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
@@ -102,6 +102,7 @@ const char *visibility[] = {
"none",
"neighbour",
"lighthouse",
+ "lighthouse",
"travel",
"far",
"unit",
@@ -1115,38 +1116,40 @@ void get_addresses(report_context * ctx)
}
for (; r != NULL; r = r->next) {
- int stealthmod = stealth_modifier(r, ctx->f, r->seen.mode);
- if (r->seen.mode == seen_lighthouse) {
- unit *u = r->units;
- for (; u; u = u->next) {
- faction *sf = visible_faction(ctx->f, u);
- if (lastf != sf) {
- if (u->building || u->ship || (stealthmod > INT_MIN
- && cansee(ctx->f, r, u, stealthmod))) {
- add_seen_faction_i(&flist, sf);
- lastf = sf;
+ if (r->seen.mode >= seen_lighthouse) {
+ int stealthmod = stealth_modifier(r, ctx->f, r->seen.mode);
+ if (r->seen.mode == seen_lighthouse) {
+ unit *u = r->units;
+ for (; u; u = u->next) {
+ faction *sf = visible_faction(ctx->f, u);
+ if (lastf != sf) {
+ if (u->building || u->ship || (stealthmod > INT_MIN
+ && cansee(ctx->f, r, u, stealthmod))) {
+ add_seen_faction_i(&flist, sf);
+ lastf = sf;
+ }
}
}
}
- }
- else if (r->seen.mode == seen_travel) {
- /* when we travel through a region, then we must add
- * the factions of any units we saw */
- add_travelthru_addresses(r, ctx->f, &flist, stealthmod);
- }
- else if (r->seen.mode > seen_travel) {
- const unit *u = r->units;
- while (u != NULL) {
- if (u->faction != ctx->f) {
- faction *sf = visible_faction(ctx->f, u);
- bool ballied = sf && sf != ctx->f && sf != lastf
- && !fval(u, UFL_ANON_FACTION) && cansee(ctx->f, r, u, stealthmod);
- if (ballied || is_allied(ctx->f, sf)) {
- add_seen_faction_i(&flist, sf);
- lastf = sf;
+ else if (r->seen.mode == seen_travel) {
+ /* when we travel through a region, then we must add
+ * the factions of any units we saw */
+ add_travelthru_addresses(r, ctx->f, &flist, stealthmod);
+ }
+ else if (r->seen.mode > seen_travel) {
+ const unit *u = r->units;
+ while (u != NULL) {
+ if (u->faction != ctx->f) {
+ faction *sf = visible_faction(ctx->f, u);
+ bool ballied = sf && sf != ctx->f && sf != lastf
+ && !fval(u, UFL_ANON_FACTION) && cansee(ctx->f, r, u, stealthmod);
+ if (ballied || is_allied(ctx->f, sf)) {
+ add_seen_faction_i(&flist, sf);
+ lastf = sf;
+ }
}
+ u = u->next;
}
- u = u->next;
}
}
}
@@ -1293,6 +1296,9 @@ static void add_seen_lighthouse(region *r, faction *f)
if (r->terrain->flags & SEA_REGION) {
add_seen_nb(f, r, seen_lighthouse);
}
+ else {
+ add_seen_nb(f, r, seen_lighthouse_land);
+ }
}
/** mark all regions seen by the lighthouse.
diff --git a/src/reports.test.c b/src/reports.test.c
index 7bea6c73c..0a85b75d2 100644
--- a/src/reports.test.c
+++ b/src/reports.test.c
@@ -573,7 +573,7 @@ static void test_prepare_lighthouse(CuTest *tc) {
CuAssertIntEquals(tc, seen_unit, r1->seen.mode);
CuAssertIntEquals(tc, seen_lighthouse, r2->seen.mode);
CuAssertIntEquals(tc, seen_neighbour, r3->seen.mode);
- CuAssertIntEquals(tc, seen_neighbour, r4->seen.mode);
+ CuAssertIntEquals(tc, seen_lighthouse_land, r4->seen.mode);
finish_reports(&ctx);
test_teardown();
}
@@ -868,6 +868,7 @@ static void test_visible_unit(CuTest *tc) {
CuAssertTrue(tc, !visible_unit(u, f, 0, seen_travel));
CuAssertTrue(tc, !visible_unit(u, f, 0, seen_none));
CuAssertTrue(tc, !visible_unit(u, f, 0, seen_neighbour));
+ CuAssertTrue(tc, !visible_unit(u, f, 0, seen_lighthouse_land));
CuAssertTrue(tc, visible_unit(u, f, 0, seen_lighthouse));
CuAssertTrue(tc, !visible_unit(u, f, -2, seen_lighthouse));
diff --git a/src/spells.c b/src/spells.c
index 6f1c5b60e..a16ec8573 100644
--- a/src/spells.c
+++ b/src/spells.c
@@ -1,7 +1,7 @@
/*
*
*
- * Eressea PB(E)M host Copyright (C) 1998-2015
+ * Eressea PB(E)M host Copyright (C) 1998-2019
* Christian Schlittchen (corwin@amber.kn-bremen.de)
* Katja Zedel (katze@felidae.kn-bremen.de)
* Henning Peters (faroul@beyond.kn-bremen.de)
@@ -1484,8 +1484,7 @@ static int sp_create_irongolem(castorder * co)
region *r = co_get_region(co);
unit *caster = co_get_caster(co);
int cast_level = co->level;
- double force = co->force;
- int number = lovar(force * 8 * RESOURCE_QUANTITY);
+ int number = (int)((co->force * 4 + lovar(co->force * 4)) * RESOURCE_QUANTITY);
static int cache;
static const race * golem_rc;
@@ -1554,7 +1553,7 @@ static int sp_create_stonegolem(castorder * co)
region *r = co_get_region(co);
unit *caster = co_get_caster(co);
int cast_level = co->level;
- int number = lovar(co->force * 5 * RESOURCE_QUANTITY);
+ int number = (int)((co->force * 2 + lovar(co->force * 3)) * RESOURCE_QUANTITY);
static int cache;
static const race * golem_rc;
diff --git a/src/spells/buildingcurse.c b/src/spells/buildingcurse.c
index 61acfbcee..b25abef34 100644
--- a/src/spells/buildingcurse.c
+++ b/src/spells/buildingcurse.c
@@ -1,6 +1,6 @@
/*
*
- * Eressea PB(E)M host Copyright (C) 1998-2015
+ * Eressea PB(E)M host Copyright (C) 1998-2019
* Christian Schlittchen (corwin@amber.kn-bremen.de)
* Katja Zedel (katze@felidae.kn-bremen.de)
* Henning Peters (faroul@beyond.kn-bremen.de)
diff --git a/src/spells/regioncurse.c b/src/spells/regioncurse.c
index d984715c1..35a09a515 100644
--- a/src/spells/regioncurse.c
+++ b/src/spells/regioncurse.c
@@ -1,6 +1,6 @@
/*
*
- * Eressea PB(E)M host Copyright (C) 1998-2015
+ * Eressea PB(E)M host Copyright (C) 1998-2019
* Christian Schlittchen (corwin@amber.kn-bremen.de)
* Katja Zedel (katze@felidae.kn-bremen.de)
* Henning Peters (faroul@beyond.kn-bremen.de)
diff --git a/src/spells/shipcurse.c b/src/spells/shipcurse.c
index 4d62d1968..278e1ec93 100644
--- a/src/spells/shipcurse.c
+++ b/src/spells/shipcurse.c
@@ -1,6 +1,6 @@
/*
*
- * Eressea PB(E)M host Copyright (C) 1998-2015
+ * Eressea PB(E)M host Copyright (C) 1998-2019
* Christian Schlittchen (corwin@amber.kn-bremen.de)
* Katja Zedel (katze@felidae.kn-bremen.de)
* Henning Peters (faroul@beyond.kn-bremen.de)
diff --git a/src/spells/unitcurse.c b/src/spells/unitcurse.c
index 1d476a7be..c3f0e05c2 100644
--- a/src/spells/unitcurse.c
+++ b/src/spells/unitcurse.c
@@ -1,6 +1,6 @@
/*
*
- * Eressea PB(E)M host Copyright (C) 1998-2015
+ * Eressea PB(E)M host Copyright (C) 1998-2019
* Christian Schlittchen (corwin@amber.kn-bremen.de)
* Katja Zedel (katze@felidae.kn-bremen.de)
* Henning Peters (faroul@beyond.kn-bremen.de)
diff --git a/src/spy.c b/src/spy.c
index 75a1e5c6a..e1e65ef95 100644
--- a/src/spy.c
+++ b/src/spy.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/steal.c b/src/steal.c
index 39158feba..08fd352db 100644
--- a/src/steal.c
+++ b/src/steal.c
@@ -1,6 +1,5 @@
/*
-Copyright (c) 1998-2014,
-Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
@@ -71,7 +70,7 @@ void expandstealing(region * r, econ_request * stealorders)
break;
}
- u = findunit(requests[j]->type.steal.no);
+ u = findunit(requests[j]->data.steal.no);
if (u && u->region == r) {
n = get_pooled(u, rsilver, GET_ALL, INT_MAX);
@@ -234,8 +233,9 @@ void steal_cmd(unit * u, struct order *ord, econ_request ** stealorders)
if (!o) abort();
o->unit = u;
o->qty = 1; /* Betrag steht in u->wants */
- o->type.steal.no = u2->no;
- o->type.steal.goblin = goblin; /* Merken, wenn Goblin-Spezialklau */
+ o->type = ECON_STEAL;
+ o->data.steal.no = u2->no;
+ o->data.steal.goblin = goblin; /* Merken, wenn Goblin-Spezialklau */
o->next = *stealorders;
*stealorders = o;
diff --git a/src/study.c b/src/study.c
index 8d39e5d5f..9b647e2ec 100644
--- a/src/study.c
+++ b/src/study.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
@@ -860,8 +860,9 @@ void reduce_skill_days(unit *u, skill_t sk, int days) {
}
}
-/** Talente von Daemonen verschieben sich.
-*/
+/**
+ * Talente von Daemonen verschieben sich.
+ */
void demon_skillchange(unit *u)
{
skill *sv = u->skills;
diff --git a/src/summary.c b/src/summary.c
index 06dabba90..1e3ddbedf 100644
--- a/src/summary.c
+++ b/src/summary.c
@@ -177,11 +177,11 @@ static int count_umlaut(const char *s)
int result = 0;
const char *cp;
for (cp = s; *cp; ++cp) {
- ucs4_t ucs = *cp;
- if (ucs & 0x80) {
+ wint_t wc = *cp;
+ if (wc & 0x80) {
size_t size;
int err;
- err = unicode_utf8_to_ucs4(&ucs, cp, &size);
+ err = unicode_utf8_decode(&wc, cp, &size);
if (err != 0) {
log_error("illegal utf8 encoding %s at %s", s, cp);
return result;
diff --git a/src/teleport.c b/src/teleport.c
index 2d8fafe72..66315d52b 100644
--- a/src/teleport.c
+++ b/src/teleport.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/tests.c b/src/tests.c
index a34e6c743..5a13c97da 100644
--- a/src/tests.c
+++ b/src/tests.c
@@ -576,15 +576,11 @@ struct message * test_find_messagetype_ex(struct message_list *msgs, const char
struct mlist *ml;
if (!msgs) return 0;
for (ml = msgs->begin; ml; ml = ml->next) {
- if (strcmp(name, test_get_messagetype(ml->msg)) == 0) {
- if (prev) {
- if (ml->msg == prev) {
- prev = NULL;
- }
- }
- else {
- return ml->msg;
- }
+ if (prev && ml->msg == prev) {
+ prev = NULL;
+ }
+ else if (strcmp(name, test_get_messagetype(ml->msg)) == 0) {
+ return ml->msg;
}
}
return 0;
diff --git a/src/travelthru.c b/src/travelthru.c
index 5a1af28eb..419c71046 100644
--- a/src/travelthru.c
+++ b/src/travelthru.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/triggers/changefaction.c b/src/triggers/changefaction.c
index c29304992..91703c552 100644
--- a/src/triggers/changefaction.c
+++ b/src/triggers/changefaction.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/triggers/changerace.c b/src/triggers/changerace.c
index 19a84eea2..0e5498663 100644
--- a/src/triggers/changerace.c
+++ b/src/triggers/changerace.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/triggers/clonedied.c b/src/triggers/clonedied.c
index c6593c2a4..861972c1f 100644
--- a/src/triggers/clonedied.c
+++ b/src/triggers/clonedied.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/triggers/createcurse.c b/src/triggers/createcurse.c
index 83f12b3be..4db37a738 100644
--- a/src/triggers/createcurse.c
+++ b/src/triggers/createcurse.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/triggers/createunit.c b/src/triggers/createunit.c
index ec1521bee..bdb8662e0 100644
--- a/src/triggers/createunit.c
+++ b/src/triggers/createunit.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/triggers/giveitem.c b/src/triggers/giveitem.c
index 3e1047f2c..6e7a7a77b 100644
--- a/src/triggers/giveitem.c
+++ b/src/triggers/giveitem.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/triggers/killunit.c b/src/triggers/killunit.c
index 0338da4af..0e49d90e2 100644
--- a/src/triggers/killunit.c
+++ b/src/triggers/killunit.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/triggers/shock.c b/src/triggers/shock.c
index 76fd6337f..a8b4774fa 100644
--- a/src/triggers/shock.c
+++ b/src/triggers/shock.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/triggers/timeout.c b/src/triggers/timeout.c
index 20c0b79fe..d9adda4dc 100644
--- a/src/triggers/timeout.c
+++ b/src/triggers/timeout.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/triggers/triggers.c b/src/triggers/triggers.c
index 13504f216..67e36381a 100644
--- a/src/triggers/triggers.c
+++ b/src/triggers/triggers.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel
diff --git a/src/util/base36.c b/src/util/base36.c
index 33701f0f6..ec41991d8 100644
--- a/src/util/base36.c
+++ b/src/util/base36.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 1998-2015, Enno Rehling
+Copyright (c) 1998-2019, Enno Rehling
Katja Zedel