diff --git a/.editorconfig b/.editorconfig
index fafe79d54..b2e1ab85b 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -15,6 +15,7 @@ indent_size = 4
[*.{xml,json}]
charset = utf-8
+indent_size = 2
# Tab indentation (no size specified)
[Makefile]
diff --git a/README.md b/README.md
index 646d036f2..4be1c324e 100644
--- a/README.md
+++ b/README.md
@@ -1,23 +1,31 @@
# What is this?
-This repository contains the source code for the Play-by-Mail strategy game [Eressea](http://www.eressea.de/).
+This repository contains the source code for the Play-by-Mail
+strategy game [Eressea](http://www.eressea.de/).
# Prerequisites
-Eressea depends on a number of external libraries. On a recent Debian-based Linux system, this is the apt-get command to install all of them:
+Eressea depends on a number of external libraries. On a recent
+Debian-based Linux system, this is the apt-get command to
+install all of them:
- sudo apt-get install git cmake gcc make libxml2-dev liblua5.2-dev libtolua-dev libncurses5-dev libsqlite3-dev luarocks
+ sudo apt-get install git cmake gcc make luarocks libxml2-dev \
+ liblua5.2-dev libtolua-dev libncurses5-dev libsqlite3-dev
# How to check out and build the Eressea server
-This repository relies heavily on the use of submodules, and it pulls in most of the code from those. The build system being used is cmake, which can create Makefiles on Unix, or Visual Studio project files on Windows. Here's how you clone and build the source on Ubuntu:
+This repository relies heavily on the use of submodules, and it pulls in
+most of the code from those. The build system being used is cmake, which
+can create Makefiles on Unix, or Visual Studio project files on Windows.
+Here's how you clone and build the source on Linux or macOS:
- git clone --recursive git://github.com/eressea/server.git
- cd server
- ./configure
- ./s/build
+ git clone --recursive git://github.com/eressea/server.git source
+ cd source
+ git submodule update --init
+ s/build
-If you got this far and all went well, you have built a server (it is linked from the `game` subdirectory), and it will have passed some basic functionality tests.
+If you got this far and all went well, you have built the server, and
+it will have passed some basic functionality tests.
* [](https://scan.coverity.com/projects/6742/)
* [](https://travis-ci.org/eressea/server)
diff --git a/conf/e2/locales.json b/conf/e2/locales.json
index 3376cfedc..02967d134 100644
--- a/conf/e2/locales.json
+++ b/conf/e2/locales.json
@@ -1,11 +1,34 @@
{
- "include": [
- "config://res/translations/strings.de.po",
- "config://res/translations/strings-e2.de.po",
- "config://res/translations/strings.en.po",
- "config://res/translations/strings-e2.en.po",
- "config://res/translations/messages.de.po",
- "config://res/translations/messages.en.po",
- "config://res/core/messages.xml"
- ]
+ "include": [
+ "config://res/translations/strings.de.po",
+ "config://res/translations/strings-e2.de.po",
+ "config://res/translations/strings.en.po",
+ "config://res/translations/strings-e2.en.po",
+ "config://res/translations/messages.de.po",
+ "config://res/translations/messages.en.po",
+ "config://res/core/messages.xml"
+ ],
+ "aliases": {
+ "de": {
+ "spell::earthquake": [
+ "Beschwöre einen Erdelementar",
+ "Beschwörung eines Erdelementares"
+ ],
+ "spell::goodwinds": [
+ "Beschwörung eines Wasserelementares",
+ "Beschwöre einen Wasserelementar"
+ ],
+ "spell::stormwinds": [
+ "Beschwöre einen Sturmelementar",
+ "Beschwörung eines Sturmelementares"
+ ],
+ "spell::summonfireelemental": [
+ "Beschwöre einen Hitzeelementar",
+ "Beschwörung eines Hitzeelementares"
+ ]
+ },
+ "en": {
+ "spell::migration": "Rit of Acceptance"
+ }
+ }
}
diff --git a/conf/e2/terrains.json b/conf/e2/terrains.json
index 5a8cf0280..bb0da7106 100644
--- a/conf/e2/terrains.json
+++ b/conf/e2/terrains.json
@@ -211,8 +211,6 @@
}
},
"firewall": {
- "size": 100,
- "road": 250,
"flags": [ "forbidden" ]
},
"fog": {
diff --git a/conf/e3/locales.json b/conf/e3/locales.json
index 27e6f93d8..2b908abc4 100644
--- a/conf/e3/locales.json
+++ b/conf/e3/locales.json
@@ -1,11 +1,11 @@
{
- "include": [
- "config://res/translations/strings.de.po",
- "config://res/translations/strings-e3.de.po",
- "config://res/translations/messages.de.po",
- "config://res/translations/strings.en.po",
- "config://res/translations/strings-e3.en.po",
- "config://res/translations/messages.en.po",
- "config://res/core/messages.xml"
- ]
+ "include": [
+ "config://res/translations/strings.de.po",
+ "config://res/translations/strings-e3.de.po",
+ "config://res/translations/messages.de.po",
+ "config://res/translations/strings.en.po",
+ "config://res/translations/strings-e3.en.po",
+ "config://res/translations/messages.en.po",
+ "config://res/core/messages.xml"
+ ]
}
diff --git a/conf/keywords.json b/conf/keywords.json
index 11376ea95..9cee06a6d 100644
--- a/conf/keywords.json
+++ b/conf/keywords.json
@@ -4,6 +4,7 @@
"plant": "PLANT",
"grow": [ "GROW", "BREED" ],
"promote": ["PROMOTE", "PROMOTION" ],
+ "locale": ["LANGUAGE", "LOCALE"],
"combat": [ "COMBAT", "FIGHT" ]
},
"de": {
@@ -65,6 +66,7 @@
"alliance": "ALLIANZ",
"claim": ["BEANSPRUCHE", "BEANSPRUCHEN"],
"promote": ["BEFÖRDERE", "BEFÖRDERUNG"],
+ "locale": ["SPRACHE", "LOCALE"],
"pay": ["BEZAHLE", "BEZAHLEN"]
}
}
diff --git a/process/checkpasswd.py b/process/checkpasswd.py
index 1e6198600..09b7b4f27 100755
--- a/process/checkpasswd.py
+++ b/process/checkpasswd.py
@@ -18,20 +18,14 @@ def log(str):
if mypasswd[0] == '"':
mypasswd = mypasswd.strip('"')
-pw_data = EPasswd()
-try:
- pw_data.load_database(filename)
- log("loaded from db " + filename)
-except:
- pw_data.load_file(filename)
- log("loaded from file " + filename)
+pw_data = EPasswd.load_any(filename, log=log)
+faction = pw_data.get_faction(myfaction)
-if pw_data.fac_exists(myfaction):
- if pw_data.check(myfaction, mypasswd):
- log("password match: " + myfaction)
- sys.exit(0)
+if not faction:
+ log("faction missing: " + myfaction)
+elif not faction.check_passwd(mypasswd):
log("password mismatch: " + myfaction)
else:
- log("faction missing: " + myfaction)
-
+ log("password match: " + myfaction)
+ sys.exit(0)
sys.exit(-1)
diff --git a/process/compress.py b/process/compress.py
index 204d580d7..0066c232f 100755
--- a/process/compress.py
+++ b/process/compress.py
@@ -15,7 +15,7 @@
addr=%(email)s
[ $# -ge 1 ] && addr=$1
-[ -z $addr ] || send-%(compression)s-report $addr '%(gamename)s Report #%(turn)s' %(files)s
+[ -z $addr ] || $ERESSEA/server/bin/send-%(compression)s-report $addr '%(gamename)s Report #%(turn)s' %(files)s
"""
turn = argv[1]
diff --git a/process/compress.sh b/process/compress.sh
index 21ac828ef..c4343d95e 100755
--- a/process/compress.sh
+++ b/process/compress.sh
@@ -5,8 +5,10 @@ if [ -z "$ERESSEA" ]; then
exit 2
fi
+BINDIR="$ERESSEA/server/bin"
+
GAME="$ERESSEA/game-$1"
-GAME_NAME=$(grep -w name "$GAME/eressea.ini" | sed 's/.*=\s*//')
+GAME_NAME=$("$BINDIR/inifile" "$GAME/eressea.ini" get game:name)
TURN=$2
if [ -z "$TURN" ]
@@ -20,6 +22,6 @@ if [ ! -d "$GAME/reports" ]; then
fi
cd "$GAME/reports" || exit
-"$ERESSEA/server/bin/compress.py" "$TURN" "$GAME_NAME"
+"$BINDIR/compress.py" "$TURN" "$GAME_NAME"
cd - || exit
diff --git a/process/cron/orders.cron b/process/cron/orders.cron
index 980f11ee2..7632ac844 100755
--- a/process/cron/orders.cron
+++ b/process/cron/orders.cron
@@ -5,11 +5,19 @@
# this here script to make a non-blocking syntax check and reject or
# accept the order file.
-if [ "yes" != "$CONFIRM" ] ; then
+if [ "no" == "$CONFIRM" ] ; then
exit
fi
-for GAME in $*
+if [ -z "$ERESSEA" ] ; then
+ ERESSEA="$HOME/eressea"
+ echo "The ERESSEA environment variable is not set. Assuming $ERESSEA."
+fi
+
+cd "$ERESSEA" || exit
+
+for GAME in "$@"
do
- $HOME/eressea/orders-php/check-orders.sh $GAME
+ orders-php/check-orders.sh "$GAME"
done
+
diff --git a/process/cron/preview.cron b/process/cron/preview.cron
index 5192d0f5a..04869c2f2 100755
--- a/process/cron/preview.cron
+++ b/process/cron/preview.cron
@@ -3,7 +3,7 @@
[ "$PREVIEW" != "yes" ] && exit
[ -z "${ERESSEA}" ] && ERESSEA="$HOME/eressea"
-eval $(luarocks path)
+eval "$(luarocks path)"
branch="develop"
if [ -e "${ERESSEA}/build/.preview" ]; then
branch=$(cat "${ERESSEA}/build/.preview")
diff --git a/process/cron/run-eressea.cron b/process/cron/run-eressea.cron
index 32ad873df..aa3ed287b 100755
--- a/process/cron/run-eressea.cron
+++ b/process/cron/run-eressea.cron
@@ -1,55 +1,55 @@
#!/bin/bash
-eval $(luarocks path)
+eval "$(luarocks path)"
GAME=$1
(
-[ "$ENABLED" != "yes" ] && exit
-[ -z ${ERESSEA} ] && ERESSEA=$HOME/eressea
+[ "$ENABLED" == "no" ] && exit
+[ -z "$ERESSEA" ] && ERESSEA="$HOME/eressea"
export ERESSEA
-BIN=$ERESSEA/server/bin
-TURN=$(cat $ERESSEA/game-$GAME/turn)
-if [ ! -e $ERESSEA/game-$GAME/data/$TURN.dat ]; then
+BIN="$ERESSEA/server/bin"
+TURN=$(cat "$ERESSEA/game-$GAME/turn")
+if [ ! -e "$ERESSEA/game-$GAME/data/$TURN.dat" ]; then
echo "data file $TURN is missing, cannot run turn for game $GAME"
exit 1
fi
-REPORTS=$ERESSEA/game-$GAME/reports
-if [ -d $REPORTS ]; then
- rm -rf $REPORTS
+REPORTS="$ERESSEA/game-$GAME/reports"
+if [ -d "$REPORTS" ]; then
+ rm -rf "$REPORTS"
fi
-mkdir $REPORTS
+mkdir "$REPORTS"
-cd $ERESSEA/game-$GAME
+cd "$ERESSEA/game-$GAME" || exit
if [ -d test ]; then
touch test/execute.lock
fi
-$BIN/create-orders $GAME $TURN
-if [ ! -s $ERESSEA/game-$GAME/orders.$TURN ]; then
+"$BIN/create-orders" "$GAME" "$TURN"
+if [ ! -s "$ERESSEA/game-$GAME/orders.$TURN" ]; then
echo "server did not create orders for turn $TURN in game $GAME"
exit 2
fi
-$BIN/backup-eressea $GAME $TURN
+"$BIN/backup-eressea" "$GAME" "$TURN"
rm -f execute.lock
-$BIN/run-turn $GAME $TURN
+"$BIN/run-turn" "$GAME" "$TURN"
touch execute.lock
-if [ ! -s $REPORTS/reports.txt ]; then
+if [ ! -s "$REPORTS/reports.txt" ]; then
echo "server did not create reports.txt in game $GAME"
exit 4
fi
-$BIN/backup-eressea $GAME $TURN
+"$BIN/backup-eressea" "$GAME" "$TURN"
let TURN=$TURN+1
-if [ ! -s $ERESSEA/game-$GAME/data/$TURN.dat ]; then
+if [ ! -s "$ERESSEA/game-$GAME/data/$TURN.dat" ]; then
echo "server did not create data for turn $TURN in game $GAME"
exit 3
fi
echo "sending reports for game $GAME, turn $TURN"
-$BIN/compress.sh $GAME $TURN
-$BIN/sendreports.sh $GAME
-$BIN/backup-eressea $GAME $TURN
+"$BIN/compress.sh" "$GAME" "$TURN"
+"$BIN/sendreports.sh" "$GAME"
+"$BIN/backup-eressea" "$GAME" "$TURN"
rm -f test/execute.lock
-) | tee -a $HOME/log/eressea.cron.log
+) | tee -a "$HOME/log/eressea.cron.log"
diff --git a/process/epasswd.py b/process/epasswd.py
index 0f38fd593..24d789fb6 100755
--- a/process/epasswd.py
+++ b/process/epasswd.py
@@ -1,63 +1,57 @@
#!/usr/bin/python
-from string import split
-from string import strip
-from string import lower
import bcrypt
+import collections
import sqlite3
-class EPasswd:
- def __init__(self):
- self.data = {}
-
- def set_data(self, no, email, passwd):
- lc_id = lower(no)
- self.data[lc_id] = {}
- self.data[lc_id]["id"] = no
- self.data[lc_id]["email"] = email
- self.data[lc_id]["passwd"] = passwd
-
- def load_database(self, file):
- conn = sqlite3.connect(file)
- c = conn.cursor()
- for row in c.execute('SELECT `no`, `email`, `password` FROM `faction`'):
- (no, email, passwd) = row
- self.set_data(no, email, passwd)
- conn.close()
-
- def load_file(self, file):
- try:
- fp = open(file,"r")
- except:
- fp = None
- if fp != None:
- while True:
- line = fp.readline()
- if not line: break
- line = strip(line)
- [id, email, passwd] = split(line, ":")[0:3]
- self.set_data(id, email, passwd)
- fp.close()
-
- def check(self, id, passwd):
- pw = self.get_passwd(id)
- if pw[0:4]=='$2a$' or pw[0:4]=='$2y$':
+
+class _Faction(collections.namedtuple('Faction', 'no email passwd')):
+ def check_passwd(self, passwd):
+ if self.passwd.startswith(('$2a$', '$2y$')):
try:
- uhash = pw.encode('utf8')
- upass = passwd.encode('utf8')
- return bcrypt.checkpw(upass, uhash)
- except:
+ return bcrypt.checkpw(passwd.encode('utf8'), self.passwd.encode('utf8'))
+ except UnicodeEncodeError:
return False
- return pw == passwd
-
- def get_passwd(self, id):
- return self.data[lower(id)]["passwd"]
-
- def get_email(self, id):
- return self.data[lower(id)]["email"]
-
- def get_canon_id(self, id):
- return self.data[lower(id)]["id"]
-
- def fac_exists(self, id):
- return self.data.has_key(lower(id))
+
+ return passwd == self.passwd
+
+
+class EPasswd:
+ def __init__(self, generator):
+ self._data = {row[0]: _Faction(*row) for row in generator}
+
+ @classmethod
+ def load_database(cls, filename):
+ conn = sqlite3.connect('file:' + filename + '?mode=ro', uri=True)
+ try:
+ cls(conn.execute('SELECT no, email, password FROM faction'))
+ finally:
+ conn.close()
+
+ @classmethod
+ def load_file(cls, filename):
+ with open(filename, 'r') as f:
+ return cls(
+ line.strip().split(':')
+ for line in f
+ if line.strip() and not line.startswith('#')
+ )
+
+ @classmethod
+ def load_any(cls, filename, passwd_filename=None, log=None):
+ try:
+ result = cls.load_database(filename)
+ type_ = "db"
+ except (sqlite3.OperationalError, sqlite3.DatabaseError):
+ if passwd_filename is not None:
+ filename = passwd_filename
+ result = cls.load_file(filename)
+ type_ = "file"
+
+ if log is not None:
+ log("loaded from " + type_ + " " + filename)
+
+ return result
+
+ def get_faction(self, no):
+ return self._data.get(no.lower())
diff --git a/process/getemail.py b/process/getemail.py
index d9951bcb0..3f5cc1d86 100755
--- a/process/getemail.py
+++ b/process/getemail.py
@@ -9,14 +9,10 @@
filename=sys.argv[1]
myfaction=sys.argv[2]
-pw_data = EPasswd()
-try:
- pw_data.load_database(filename)
-except:
- pw_data.load_file(filename)
+pw_data = EPasswd.load_any(filename)
+faction = pw_data.get_faction(myfaction)
-if pw_data.fac_exists(myfaction):
- email = pw_data.get_email(myfaction)
- print(email)
- sys.exit(0)
-sys.exit(-1)
+if not faction:
+ sys.exit(-1)
+
+print(faction.email)
diff --git a/process/getfaction.py b/process/getfaction.py
index 11350ec8a..92880a7b3 100755
--- a/process/getfaction.py
+++ b/process/getfaction.py
@@ -14,19 +14,12 @@ def log(str):
if not quiet:
print(str)
-pw_data = EPasswd()
-try:
- pw_data.load_database(filename)
- log("loaded from db " + filename)
-except:
- pw_data.load_file(filename)
- log("loaded from file " + filename)
+pw_data = EPasswd.load_any(filename, log=log)
+faction = pw_data.get_faction(myfaction)
-if pw_data.fac_exists(myfaction):
- print(pw_data.get_email(myfaction))
- log("faction found: " + myfaction)
- sys.exit(0)
-else:
+if not faction:
log("faction missing: " + myfaction)
+ sys.exit(-1)
-sys.exit(-1)
+print(faction.email)
+log("faction found: " + myfaction)
diff --git a/process/orders-process b/process/orders-process
index 4751a94b1..d77b7fa2f 100755
--- a/process/orders-process
+++ b/process/orders-process
@@ -1,6 +1,7 @@
#!/bin/sh
-SCRIPT=$(readlink -f $0)
-cd $(dirname $SCRIPT)
+SCRIPT=$(readlink -f "$0")
+SCRDIR=$(dirname "$SCRIPT")
+cd "$SCRDIR" || exit
lockfile -r3 -l120 orders.queue.lock
python process-orders.py "$@"
diff --git a/process/process-orders.py b/process/process-orders.py
index dade32c02..722b50ec7 100755
--- a/process/process-orders.py
+++ b/process/process-orders.py
@@ -82,15 +82,13 @@ def check_pwd(filename, email, pw_data):
if mo != None:
fact_nr = str(mo.group(2))
fact_pw = str(mo.group(3))
- if pw_data.fac_exists(fact_nr):
- if not pw_data.check(fact_nr, fact_pw):
- game_email = pw_data.get_email(fact_nr)
- results = results + [ (fact_nr, game_email, False, fact_pw) ]
- else:
- game_email = pw_data.get_email(fact_nr)
- results = results + [ (fact_nr, game_email, True, fact_pw) ]
+ faction = pw_data.get_faction(fact_nr)
+
+ if not faction:
+ results.append((fact_nr, None, False, fact_pw))
else:
- results = results + [ (fact_nr, None, False, fact_pw) ]
+ results.append((fact_nr, faction.email, faction.check_passwd(fact_pw), fact_pw))
+
return results
def echeck(filename, locale, rules):
@@ -108,11 +106,10 @@ def echeck(filename, locale, rules):
return mail
#print "reading password file..."
-pw_data = EPasswd()
-try:
- pw_data.load_database(os.path.join(game_dir, "eressea.db"))
-except:
- pw_data.load_file(os.path.join(game_dir, "passwd"))
+pw_data = EPasswd.load_any(
+ os.path.join(game_dir, "eressea.db"),
+ os.path.join(game_dir, "passwd"),
+)
#print "reading orders.queue..."
# move the queue file to a safe space while locking it:
diff --git a/process/received-mail.sh b/process/received-mail.sh
deleted file mode 100644
index b6b4a0605..000000000
--- a/process/received-mail.sh
+++ /dev/null
@@ -1 +0,0 @@
-ls -1 orders.dir/turn* | sed -e 's/.*turn-\(.*\),gruenbaer.*/\1/' | sort -u
diff --git a/process/received.sh b/process/received.sh
deleted file mode 100644
index 9a0c4511d..000000000
--- a/process/received.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-grep -hiw ERESSEA orders.dir/turn-* | cut -d\ -f2 | sort -u
diff --git a/res/core/messages.xml b/res/core/messages.xml
index 1fd1c106f..e85787a95 100644
--- a/res/core/messages.xml
+++ b/res/core/messages.xml
@@ -247,14 +247,14 @@
-
+
-
+
@@ -1124,6 +1124,12 @@
+
+
+
+
+
+
@@ -1299,6 +1305,14 @@
+
+
+
+
+
+
+
+
@@ -2739,6 +2753,14 @@
+
+
+
+
+
+
+
+
@@ -3477,6 +3499,13 @@
+
+
+
+
+
+
+
@@ -4961,6 +4990,14 @@
+
+
+
+
+
+
+
+
@@ -5451,13 +5488,6 @@
-
-
-
-
-
-
-
@@ -5477,16 +5507,6 @@
-
-
-
-
-
-
-
-
-
-
@@ -5800,13 +5820,6 @@
-
-
-
-
-
-
-
diff --git a/res/e3a/races.xml b/res/e3a/races.xml
index 2316b8aa5..d039b067d 100644
--- a/res/e3a/races.xml
+++ b/res/e3a/races.xml
@@ -192,7 +192,7 @@
-
+
@@ -218,7 +218,7 @@
-
+
@@ -239,7 +239,7 @@
-
+
@@ -260,7 +260,7 @@
-
+
@@ -283,7 +283,7 @@
-
+
@@ -306,7 +306,7 @@
-
+
@@ -328,7 +328,7 @@
-
+
@@ -400,7 +400,7 @@
-
+
@@ -425,7 +425,7 @@
-
+
@@ -448,7 +448,7 @@
-
+
@@ -471,7 +471,7 @@
-
+
@@ -493,7 +493,7 @@
-
+
@@ -520,7 +520,7 @@
-
+
@@ -543,7 +543,7 @@
-
+
@@ -565,7 +565,7 @@
-
+
@@ -589,7 +589,7 @@
-
+
@@ -651,11 +651,11 @@
-
+
-
+
@@ -678,11 +678,7 @@
-
-
-
-
-
+
@@ -694,7 +690,7 @@
-
+
@@ -710,7 +706,7 @@
-
+
@@ -830,7 +826,7 @@
-
+
diff --git a/res/e3a/weapons.xml b/res/e3a/weapons.xml
index 3eea6dc44..f993b7ac2 100644
--- a/res/e3a/weapons.xml
+++ b/res/e3a/weapons.xml
@@ -3,9 +3,9 @@
- -
+
-
@@ -112,7 +112,7 @@
-
-
+
-
diff --git a/res/eressea/races.xml b/res/eressea/races.xml
index 71eacc458..ac182a2f8 100644
--- a/res/eressea/races.xml
+++ b/res/eressea/races.xml
@@ -11,7 +11,7 @@
-
+
@@ -26,7 +26,7 @@
-
+
@@ -59,7 +59,7 @@
-
+
@@ -87,7 +87,7 @@
-
+
@@ -114,7 +114,7 @@
-
+
@@ -143,7 +143,7 @@
-
+
@@ -172,7 +172,7 @@
-
+
@@ -202,7 +202,7 @@
-
+
@@ -236,7 +236,7 @@
-
+
@@ -266,7 +266,7 @@
-
+
@@ -298,7 +298,7 @@
-
+
@@ -328,7 +328,7 @@
-
+
@@ -357,7 +357,7 @@
-
+
@@ -387,7 +387,7 @@
-
+
@@ -416,7 +416,7 @@
-
+
@@ -447,7 +447,7 @@
-
+
@@ -478,7 +478,7 @@
-
+
@@ -507,7 +507,7 @@
-
+
@@ -539,7 +539,7 @@
-
+
@@ -569,16 +569,16 @@
-
+
-
+
-
+
@@ -648,60 +648,56 @@
-
+
-
+
-
+
-
+
-
+
-
-
-
-
-
+
-
+
-
+
-
+
@@ -711,7 +707,7 @@
-
+
@@ -721,7 +717,7 @@
-
+
@@ -732,7 +728,7 @@
-
+
@@ -812,7 +808,7 @@
-
+
@@ -903,7 +899,7 @@ giveunit="yes" getitem="yes" recruitethereal="yes" equipment="yes">
-
+
@@ -927,7 +923,7 @@ giveunit="yes" getitem="yes" recruitethereal="yes" equipment="yes">
-
+
@@ -964,7 +960,7 @@ giveunit="yes" getitem="yes" recruitethereal="yes" equipment="yes">
-
+
@@ -972,7 +968,7 @@ giveunit="yes" getitem="yes" recruitethereal="yes" equipment="yes">
-
+
@@ -986,7 +982,7 @@ giveunit="yes" getitem="yes" recruitethereal="yes" equipment="yes">
-
+
@@ -1003,7 +999,7 @@ giveunit="yes" getitem="yes" recruitethereal="yes" equipment="yes">
-
+
@@ -1019,7 +1015,7 @@ giveunit="yes" getitem="yes" recruitethereal="yes" equipment="yes">
-
+
@@ -1034,7 +1030,7 @@ giveunit="yes" getitem="yes" recruitethereal="yes" equipment="yes">
-
+
@@ -1047,7 +1043,7 @@ giveunit="yes" getitem="yes" recruitethereal="yes" equipment="yes">
-
+
@@ -1061,7 +1057,7 @@ giveunit="yes" getitem="yes" recruitethereal="yes" equipment="yes">
-
+
@@ -1074,11 +1070,11 @@ giveunit="yes" getitem="yes" recruitethereal="yes" equipment="yes">
-
+
-
+
@@ -1205,7 +1201,7 @@ giveunit="yes" getitem="yes" recruitethereal="yes" equipment="yes">
-
+
diff --git a/res/translations/messages.de.po b/res/translations/messages.de.po
index ae0f13dd4..6039f9481 100644
--- a/res/translations/messages.de.po
+++ b/res/translations/messages.de.po
@@ -641,6 +641,9 @@ msgstr "\"$unit($unit) vergisst $skill($skill).\""
msgid "spell_astral_only"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Dieser Zauber kann nur im Astralraum gezaubert werden.\""
+msgid "spell_astral_forbidden"
+msgstr "\"$unit($unit) in $region($region): '$order($command)' - Dieser Zauber kann nicht im Astralraum gezaubert werden.\""
+
msgid "sp_movecastle_fail_0"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Der Elementar ist zu klein, um das Gebäude zu tragen.\""
@@ -653,7 +656,7 @@ msgstr "\"$unit($unit) ertrinkt in $region($region).\""
msgid "travel"
msgstr "\"$unit($unit) $if($eq($mode,1),\"reitet\", \"wandert\") von $region($start) nach $region($end).$if($isnull($regions),\"\",\" Dabei wurde $trail($regions) durchquert.\")\""
-msgid "curseinfo::skill_1"
+msgid "curseinfo_skill_1"
msgstr "\"$unit($unit) ist ungewöhnlich geschickt in $skill($skill). ($int36($id))\""
msgid "error11"
@@ -1250,6 +1253,9 @@ msgstr "\"$unit($unit) verdient in $region($region) $int($amount) Silber durch U
msgid "error180"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Der Zauber schlägt fehl.\""
+msgid "sp_shapeshift_twice"
+msgstr "\"$unit($unit) in $region($region): '$order($command)' - $unit($target) ist bereits verzaubert.\""
+
msgid "sp_shapeshift_fail"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - $unit($target) kann nicht $race($race,1) werden.\""
@@ -1383,7 +1389,7 @@ msgid "earthquake_effect"
msgstr "\"$unit($mage) läßt die Erde in $region($region) erzittern.\""
msgid "error56"
-msgstr "\"$unit($unit) in $region($region): '$order($command)' - Die Einheit kann soviele Pferde nicht bändigen.\""
+msgstr "\"$unit($unit) in $region($region): '$order($command)' - Die Einheit kann so viele Pferde nicht bändigen.\""
msgid "error78"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Ein Fluch verhindert die Übergabe.\""
@@ -1481,12 +1487,9 @@ msgstr "\"Ein magischer Schimmer liegt auf diesen Mauern. ($int36($id))\""
msgid "changebanner"
msgstr "\"Das Banner wurde auf '$value' geändert.\""
-msgid "curseinfo::skill_2"
+msgid "curseinfo_skill_2"
msgstr "\"$unit($unit) ist ungewöhnlich ungeschickt in $skill($skill). ($int36($id))\""
-msgid "spellfail::noway"
-msgstr "\"$unit($unit) in $region($region): '$order($command)' - Dorthin führt kein Weg.\""
-
msgid "spellbuildingresists"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Gebäude $int36($id) konnte nicht verzaubert werden.\""
@@ -1634,9 +1637,6 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - $unit($unit) ge
msgid "curseinfo::sparkle_11"
msgstr "\"Vogelzwitschern begleitet $unit($unit) auf all seinen Wegen. ($int36($id))\""
-msgid "wdw_pyramidspell_notfound"
-msgstr "\"$unit($unit) in $region($region): '$order($command)' - In dieser Region können keine Pyramiden gebaut werden. Die nächste Pyramidenregion ist zwischen $int($mindist) und $int($maxdist) Regionen entfernt.\""
-
msgid "sailforbidden"
msgstr "\"Die Mannschaft der $ship($ship) weigert sich, nach $region($region) zu reisen.\""
@@ -1859,9 +1859,6 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - Die Wege zwisch
msgid "familiar_toofar"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - $unit($mage) kann nicht genug Energie aufbringen, um diesen Spruch durch $unit($unit) zu wirken.\""
-msgid "wdw_pyramidspell_found"
-msgstr "\"$unit($unit) in $region($region): '$order($command)' - In dieser Regione können Pyramiden gebaut werden.\""
-
msgid "deorcified"
msgstr "\"Langsam kehren andere Völker nach $region($region) zurück.\""
@@ -2037,7 +2034,7 @@ msgid "analyse_building_fail"
msgstr "\"$unit($mage) meint, dass auf $building($building) ein Zauber liegt, konnte aber über den Zauber nichts herausfinden.\""
msgid "spellfail_toomanytargets"
-msgstr "\"$unit($unit) in $region($region): '$order($command)' - So viele Persoenen übersteigen die Kräfte des Magiers.\""
+msgstr "\"$unit($unit) in $region($region): '$order($command)' - So viele Personen übersteigen die Kräfte des Magiers.\""
msgid "harvest_effect"
msgstr "\"$if($isnull($mage),\"Ein unentdeckter Magier\",$unit($mage)) segnet in einem kurzen Ritual die Felder.\""
@@ -2522,6 +2519,9 @@ msgstr "\"$unit($mage) meint, dass auf $region($region) ein Zauber liegt, konnte
msgid "viewreality_effect"
msgstr "\"$unit($unit) gelingt es, durch die Nebel auf die Realität zu blicken.\""
+msgid "showastral_effect"
+msgstr "\"$unit($unit) gelingt es, in die Nebel des Astralraums zu blicken.\""
+
msgid "use_speedsail"
msgstr "\"$unit($unit) setzt ein Sonnensegel. Die Geschwindigkeit des Schiffes erhöht um $int($speed).\""
@@ -2777,6 +2777,12 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - So viele Schiff
msgid "error328"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Dafür müssen die Schiffe an derselben Küste liegen.\""
+msgid "error329"
+msgstr "\"$unit($unit) in $region($region): '$order($command)' - Die Einheit ist zu klein für einen Konvoi dieser Größe.\""
+
+msgid "give_number_missing"
+msgstr "\"$unit($unit) in $region($region): '$order($command)' - Die Anzahl der $localize($resource) fehlt.\""
+
msgid "error326"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Diese Schiffe können keinen Konvoi bilden.\""
diff --git a/res/translations/messages.en.po b/res/translations/messages.en.po
index b140f1690..bd9e7b842 100644
--- a/res/translations/messages.en.po
+++ b/res/translations/messages.en.po
@@ -594,7 +594,7 @@ msgid "error206"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - There is alrady a spell on that building.\""
msgid "error316"
-msgstr "\"$unit($unit) in $region($region): '$order($command)' - Without ingredients an alchemist can not produce anything.\""
+msgstr "\"$unit($unit) in $region($region): '$order($command)' - Without ingredients an alchemist cannot produce anything.\""
msgid "error312"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Lycantropes may not be mixed with normal people.\""
@@ -638,6 +638,9 @@ msgstr "\"$unit($unit) in $region($region) damages the $ship($ship).\""
msgid "forget"
msgstr "\"$unit($unit) forgets $skill($skill).\""
+msgid "spell_astral_forbidden"
+msgstr "\"$unit($unit) in $region($region): '$order($command)' - This spell cannot be cast on the astral plane.\""
+
msgid "spell_astral_only"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - This spell can only be cast on the astral plane.\""
@@ -653,7 +656,7 @@ msgstr "\"$unit($unit) drowns in $region($region).\""
msgid "travel"
msgstr "\"$unit($unit) $if($eq($mode,1),\"rides\", \"walks\") from $region($start) to $region($end)$if($isnull($regions),\"\",\" by way of $trail($regions)\").\""
-msgid "curseinfo::skill_1"
+msgid "curseinfo_skill_1"
msgstr "\"$unit($unit) is incredibly skilled at $skill($skill). ($int36($id))\""
msgid "error11"
@@ -864,7 +867,7 @@ msgid "curseinfo::shipnodrift_1"
msgstr "\"The $ship($ship) is blessed with favourable winds$if($lt($duration,3),\", but the spell is starting to wear thin\",\"\"). ($int36($id))\""
msgid "error105"
-msgstr "\"$unit($unit) in $region($region): '$order($command)' - Empty units can not be handed over.\""
+msgstr "\"$unit($unit) in $region($region): '$order($command)' - Empty units cannot be handed over.\""
msgid "curseinfo::sparkle_7"
msgstr "\"The women of the nearby village cast furtive looks at $unit($unit). ($int36($id))\""
@@ -1164,7 +1167,7 @@ msgid "error_max_magicians"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - There may not be more than $int($amount) magicians in your faction.\""
msgid "error304"
-msgstr "\"$unit($unit) in $region($region): '$order($command)' - Units of a faction that can't be attacked may not guard.\""
+msgstr "\"$unit($unit) in $region($region): '$order($command)' - Units of a faction that cannot be attacked may not guard.\""
msgid "error300"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Invalid synonym.\""
@@ -1253,6 +1256,9 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - The spell fails
msgid "sp_shapeshift_fail"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - $unit($target) cannot take the form of $race($race,1).\""
+msgid "sp_shapeshift_twice"
+msgstr "\"$unit($unit) in $region($region): '$order($command)' - $unit($target) is already under this spell.\""
+
msgid "error290"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Eine Einheit kann nur in einem Verband Mitglied sein.\""
@@ -1481,12 +1487,9 @@ msgstr "\"A magical shimmer lies on these walls. ($int36($id))\""
msgid "changebanner"
msgstr "\"Banner has been changed to '$value'.\""
-msgid "curseinfo::skill_2"
+msgid "curseinfo_skill_2"
msgstr "\"$unit($unit) has some troubles with $skill($skill). ($int36($id))\""
-msgid "spellfail::noway"
-msgstr "\"$unit($unit) in $region($region): '$order($command)' - There is no route leading there.\""
-
msgid "spellbuildingresists"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Building $int36($id) could not be charmed.\""
@@ -1634,9 +1637,6 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - $unit($unit) ma
msgid "curseinfo::sparkle_11"
msgstr "\"Bird songs follow $unit($unit) on all his travels. ($int36($id))\""
-msgid "wdw_pyramidspell_notfound"
-msgstr "\"$unit($unit) in $region($region): '$order($command)' - No pyramids may be build in this region. The closest region to build a pyramid in is between $int($mindist) and $int($maxdist) regions away.\""
-
msgid "sailforbidden"
msgstr "\"The crew of the $ship($ship) refuses to travel to $region($region).\""
@@ -1859,9 +1859,6 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - The paths to th
msgid "familiar_toofar"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - $unit($mage) cannot raise enough energy to channel the spell through $unit($unit).\""
-msgid "wdw_pyramidspell_found"
-msgstr "\"$unit($unit) in $region($region): '$order($command)' - Pyramids may be build in this region.\""
-
msgid "deorcified"
msgstr "\"Little by little, people return to $region($region).\""
@@ -2304,7 +2301,7 @@ msgid "sailnolanding"
msgstr "\"The $ship($ship) could not berth in $region($region). The coast is too dangerous for the vessel.\""
msgid "error116"
-msgstr "\"$unit($unit) in $region($region): '$order($command)' - Number can not be assigned.\""
+msgstr "\"$unit($unit) in $region($region): '$order($command)' - Number cannot be assigned.\""
msgid "error112"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Names may not contain parenthesis.\""
@@ -2519,6 +2516,9 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - The magician de
msgid "analyse_region_fail"
msgstr "\"It appears to $unit($mage) that $region($region) is charmed, but no details have been revealed.\""
+msgid "showastral_effect"
+msgstr "\"$unit($unit) has a vision of the astral plane.\""
+
msgid "viewreality_effect"
msgstr "\"$unit($unit) manages to catch a glimpse of reality through the fog.\""
@@ -2637,7 +2637,7 @@ msgid "seduce_effect_0"
msgstr "\"$unit($unit) gives $unit($mage) $resources($items).\""
msgid "error311"
-msgstr "\"$unit($unit) in $region($region): '$order($command)' - This unit can not change shape.\""
+msgstr "\"$unit($unit) in $region($region): '$order($command)' - This unit cannot change shape.\""
msgid "error201"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Race and target unit have not been supplied.\""
@@ -2780,6 +2780,12 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - The ship is und
msgid "error328"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - All ships of a convoy must be on the same coast.\""
+msgid "error329"
+msgstr "\"$unit($unit) in $region($region): '$order($command)' - The unit has too few members to sail this convoi.\""
+
+msgid "give_number_missing"
+msgstr "\"$unit($unit) in $region($region): '$order($command)' - Missign number of $localize($resource,0).\""
+
msgid "dissolve_units_2"
msgstr "\"$unit($unit) in $region($region): $int($number) $race($race,$number) turned into $if($eq($number,1),\"a tree\", \"trees\").\""
diff --git a/res/translations/strings.de.po b/res/translations/strings.de.po
index ba16c31e9..6a03cce5a 100644
--- a/res/translations/strings.de.po
+++ b/res/translations/strings.de.po
@@ -106,28 +106,16 @@ msgctxt "keyword"
msgid "entertain"
msgstr "UNTERHALTE"
-msgid "spinx00"
-msgstr "Das Schiff des Elfen hat ein rotes Segel"
-
msgid "greatbow_p"
msgstr "Elfenbögen"
-msgid "spinx01"
-msgstr "Der Zwerg hat eine Nuss dabei"
-
msgid "person"
msgstr "Person"
-msgid "spinx02"
-msgstr "Die Katze führt eine Hellebarde"
-
msgctxt "spell"
msgid "eternal_walls"
msgstr "Mauern der Ewigkeit"
-msgid "spinx03"
-msgstr "Das Schiff mit dem grünen Segel liegt links neben dem mit einem weissen Segel"
-
msgid "JEDEM"
msgstr "JEDEM"
@@ -135,31 +123,9 @@ msgctxt "skill"
msgid "sailing"
msgstr "Segeln"
-msgid "spinx04"
-msgstr "Auf dem Schiff mit grünen Segeln kam der Speerkämpfer"
-
msgid "h16_p"
msgstr "Spaltwachse"
-msgid "spinx05"
-msgstr "Der Krieger mit dem Kreis im Wappen hat einen Keks"
-
-msgid "spinx06"
-msgstr "Der Krieger des mittleren Schiffs hat ein Schwert"
-
-msgid "fortress_generic"
-msgstr "Burg"
-
-msgid "spinx07"
-msgstr "Auf dem gelben Segel prankt ein Kreuz als Wappen"
-
-msgctxt "race"
-msgid "undeadpharaoh_d"
-msgstr "Untoten Pharao"
-
-msgid "spinx08"
-msgstr "Der Mensch kam mit dem ersten Schiff"
-
msgid "TRAENKE"
msgstr "TRÄNKE"
@@ -201,14 +167,6 @@ msgctxt "iteminfo"
msgid "wente_dress"
msgstr "Hach! Sieht der Mann beeindruckend aus in diesem Frack! Und so ordentlich! Und so ernst! Und so beeindruckend! Es fällt ein wenig schwer, sich auf den Bräutigam zu konzentrieren, weil das Brautkleid noch daneben strahlt, aber der Anzug des Bräutigams ist auf jeden Fall so, wie er sein soll und sieht toll aus und sehr geschmackvoll."
-msgctxt "race"
-msgid "apophis_d"
-msgstr "Apophis"
-
-msgctxt "race"
-msgid "undeadpharaoh_p"
-msgstr "Untoter Pharaonen"
-
msgctxt "iteminfo"
msgid "magicbag"
msgstr "Dieser Beutel umschließt eine kleine Dimensionsfalte, in der bis zu 200 Gewichtseinheiten transportiert werden können, ohne dass sie auf das Traggewicht angerechnet werden. Pferde und andere Lebewesen sowie besonders sperrige Dinge (Wagen und Katapulte) können nicht in dem Beutel transportiert werden. Auch ist es nicht möglich, einen Zauberbeutel in einem anderen zu transportieren. Der Beutel selber wiegt 1 GE."
@@ -239,10 +197,6 @@ msgctxt "calendar"
msgid "age_3"
msgstr "des dritten Zeitalters"
-msgctxt "race"
-msgid "undeadpharaoh_x"
-msgstr "Untote Pharaonen"
-
msgctxt "race"
msgid "youngdragon_d"
msgstr "Jungdrachen"
@@ -262,17 +216,10 @@ msgctxt "spellinfo"
msgid "auraleak"
msgstr "Der Schwarzmagier kann mit diesem dunklen Ritual einen Riss in das Gefüge der Magie bewirken, der alle magische Kraft aus der Region reißen wird. Alle magisch begabten in der Region werden einen Großteil ihrer Aura verlieren."
-msgctxt "race"
-msgid "apophis_p"
-msgstr "Apophis"
-
msgctxt "spell"
msgid "calm_monster"
msgstr "Monster friedlich stimmen"
-msgid "spinx10"
-msgstr "Das Schiff des Kriegers, der ein Apfel hat, liegt neben dem, der ein Kreuz als Wappen hat"
-
msgctxt "spellinfo"
msgid "sound_out"
msgstr "Erliegt die Einheit dem Zauber, so wird sie dem Magier alles erzählen, was sie über die gefragte Region weiß. Ist in der Region niemand ihrer Partei, so weiß sie nichts zu berichten. Auch kann sie nur das erzählen, was sie selber sehen könnte."
@@ -281,26 +228,10 @@ msgctxt "spell"
msgid "readmind"
msgstr "Traumdeuten"
-msgid "spinx11"
-msgstr "Der Krieger mit dem Turm im Wappen trägt eine Axt"
-
-msgid "spinx12"
-msgstr "Das Schiff des Menschen liegt neben dem blauen Schiff"
-
-msgctxt "race"
-msgid "apophis_x"
-msgstr "Apophis"
-
-msgid "spinx13"
-msgstr "Das Insekt trägt einen Baum als Wappen"
-
msgctxt "race"
msgid "youngdragon_p"
msgstr "Jungdrachen"
-msgid "spinx14"
-msgstr "Das Schiff mit dem Stern im Wappen liegt neben dem des Kriegers, der einen Zweihänder führt"
-
msgid "nr_options"
msgstr "Optionen"
@@ -705,10 +636,6 @@ msgstr "Dunkle"
msgid "zombie_prefix_8"
msgstr "Fürchterliche"
-msgctxt "race"
-msgid "redscarab"
-msgstr "roter Scarabäus"
-
msgctxt "race"
msgid "wolf_d"
msgstr "Wölfen"
@@ -893,10 +820,6 @@ msgctxt "race"
msgid "shadowmaster_d"
msgstr "Schattenmeistern"
-msgctxt "race"
-msgid "redscarab_d"
-msgstr "roten Scarabäen"
-
msgid "aurapotion50_p"
msgstr "Auratränke"
@@ -919,9 +842,6 @@ msgctxt "spellinfo"
msgid "disturbingdreams"
msgstr "Dieser Zauber führt in der betroffenen Region für einige Wochen zu Schlaflosigkeit und Unruhe. Den Betroffenen fällt das Lernen deutlich schwerer."
-msgid "wdw_pyramid"
-msgstr "Pyramide"
-
msgctxt "race"
msgid "shadowmaster_p"
msgstr "Schattenmeister"
@@ -932,10 +852,6 @@ msgstr "unbewaffnet"
msgid "plain"
msgstr "Ebene"
-msgctxt "race"
-msgid "redscarab_p"
-msgstr "rote Scarabäen"
-
msgctxt "race"
msgid "seaserpent_p"
msgstr "Seeschlangen"
@@ -968,10 +884,6 @@ msgstr "Adamantium"
msgid "seashell"
msgstr "Muschel"
-msgctxt "race"
-msgid "redscarab_x"
-msgstr "rote Scarabäen"
-
msgid "adamantiumplate"
msgstr "Adamantiumrüstung"
@@ -1009,10 +921,6 @@ msgstr "Ritual der Aufnahme"
msgid "SCHIFF"
msgstr "SCHIFF"
-msgctxt "race"
-msgid "littlescarab"
-msgstr "kleiner Scarabäus"
-
msgctxt "iteminfo"
msgid "nut"
msgstr "Nuß, im umgangssprachlichen Sinne alle trockenen, hartschaligen Früchte oder Samen, die eine Schale besitzen, die sich leicht vom inneren, eßbaren Kern entfernen läßt. In der botanischen Terminologie beschränkt sich die Bezeichnung Nuß auf eine einsamige Frucht, die aus einem Fruchtknoten (Ovarium) entstanden ist, dessen äußere Wände sich verholzt haben und der sich nicht öffnet, um seinen Samen zu entlassen. Solche echten Nüsse können eßbar, aber auch ungenießbar sein. Bekannte Beispiele sind Eicheln, Bucheckern, Kastanien und Haselnüsse. Beispiele für Früchte oder Samen, die vom Volksmund fälschlich als Nüsse bezeichnet werden, sind Mandeln und Walnüsse: Im botanischen Sinne sind dies Steinfrüchte, denen die fleischige äußere Schale entfernt wurde. Andere Beispiele für unechte Nüsse sind Erdnüsse - in Hülsen eingeschlossene Samen - sowie Roßkastanien und Paranüsse, bei denen es sich um von Kapseln umhüllte Samen handelt."
@@ -1121,6 +1029,12 @@ msgstr "ein Boot"
msgid "boat_p"
msgstr "Boote"
+msgid "ship"
+msgstr "Schiff"
+
+msgid "ship_p"
+msgstr "Schiffe"
+
msgctxt "race"
msgid "nymph"
msgstr "Nymphe"
@@ -1267,7 +1181,7 @@ msgstr "Dieser Zauber bewirkt eine schwere Störung des Astralraums. Innerhalb e
msgctxt "spell"
msgid "earthquake"
-msgstr "Beschwöre einen Erdelementar"
+msgstr "Erdelementar"
msgctxt "spell"
msgid "raise_mob"
@@ -1646,10 +1560,6 @@ msgstr "Adamantiumrüstungen"
msgid "log"
msgstr "Holz"
-msgctxt "race"
-msgid "bluescarab"
-msgstr "blauer Scarabäus"
-
msgctxt "school"
msgid "draig"
msgstr "Draig"
@@ -1685,10 +1595,6 @@ msgctxt "spell"
msgid "mallorntreegrow"
msgstr "Segne Mallornstecken"
-msgctxt "race"
-msgid "littlescarab_d"
-msgstr "kleinen Scarabäen"
-
msgctxt "keyword"
msgid "hide"
msgstr "TARNE"
@@ -1708,10 +1614,6 @@ msgctxt "damage"
msgid "plusstrong"
msgstr "sehr stark"
-msgctxt "race"
-msgid "littlescarab_p"
-msgstr "kleine Scarabäen"
-
msgid "an_unknown_ship"
msgstr "ein unbekanntes Schiff"
@@ -1746,7 +1648,7 @@ msgstr "DEBUG"
msgctxt "spell"
msgid "goodwinds"
-msgstr "Beschwörung eines Wasserelementares"
+msgstr "Wasserelementar"
msgid "GEBAEUDE"
msgstr "GEBÄUDE"
@@ -1758,10 +1660,6 @@ msgctxt "spellinfo"
msgid "rustweapon"
msgstr "Mit diesem Ritual wird eine dunkle Gewitterfront beschworen, die sich unheilverkündend über der Region auftürmt. Der magische Regen wird alles Erz rosten lassen. Eisenwaffen und Rüstungen werden schartig und rostig. Die Zerstörungskraft des Regens ist von der investierten Kraft des Magiers abhängig. Für jede Stufe können bis zu 10 Eisenwaffen betroffen werden. Ein Ring der Macht verstärkt die Wirkung wie eine zusätzliche Stufe."
-msgctxt "race"
-msgid "littlescarab_x"
-msgstr "kleine Scarabäen"
-
msgid "rop"
msgstr "Ring der Macht"
@@ -2282,10 +2180,6 @@ msgctxt "spell"
msgid "wdwpyramid_illaun"
msgstr "Traum von den Göttern"
-msgctxt "race"
-msgid "bluescarab_d"
-msgstr "blauen Scarabäen"
-
msgctxt "race"
msgid "snotling"
msgstr "Snotling"
@@ -2317,10 +2211,6 @@ msgctxt "prefix"
msgid "Klein"
msgstr "Klein"
-msgctxt "race"
-msgid "bluescarab_p"
-msgstr "blaue Scarabäen"
-
msgid "citadel"
msgstr "Zitadelle"
@@ -2344,10 +2234,6 @@ msgstr "Wyrme"
msgid "viele"
msgstr "viele"
-msgctxt "race"
-msgid "bluescarab_x"
-msgstr "blaue Scarabäen"
-
msgctxt "spell"
msgid "wdwpyramid_draig"
msgstr "Göttliche Macht"
@@ -2947,7 +2833,7 @@ msgstr "aus dem Dunkel"
msgctxt "spell"
msgid "drought"
-msgstr "Beschwörung eines Hitzeelementar"
+msgstr "Tor in die Ebene der Hitze"
msgctxt "describe"
msgid "p6"
@@ -3653,7 +3539,7 @@ msgstr "FAHRE"
msgctxt "spell"
msgid "stormwinds"
-msgstr "Beschwöre einen Sturmelementar"
+msgstr "Sturmelementar"
msgctxt "race"
msgid "elf"
@@ -3812,10 +3698,6 @@ msgstr "Silber"
msgid "jadee_ring_p"
msgstr "Jadees Hochzeitsringe"
-msgctxt "race"
-msgid "apophis"
-msgstr "Apophis"
-
msgctxt "shipinfo"
msgid "no_info"
msgstr "Keine Informationen über diesen Schiffstyp verfügbar."
@@ -4086,10 +3968,6 @@ msgstr "Neue Zauber"
msgid "nr_borderlist_prefix"
msgstr "Im "
-msgctxt "race"
-msgid "greenscarab_d"
-msgstr "grünen Scarabäen"
-
msgctxt "iteminfo"
msgid "speedsail"
msgstr "Setzt eine Einheit dieses Segel auf einem Schiff, so erhöht sich dessen Reichweite permanent um 1 Region."
@@ -4102,10 +3980,6 @@ msgctxt "spell"
msgid "flee"
msgstr "Grauen der Schlacht"
-msgctxt "race"
-msgid "greenscarab_p"
-msgstr "grüne Scarabäen"
-
msgid "studypotion_p"
msgstr "Lerntränke"
@@ -4133,10 +4007,6 @@ msgctxt "race"
msgid "human_d"
msgstr "Menschen"
-msgctxt "race"
-msgid "greenscarab_x"
-msgstr "grünen Scarabäen"
-
msgctxt "spellinfo"
msgid "unholypower"
msgstr "Nur geflüstert wird dieses Ritual an den dunklen Akademien an die Adepten weitergegeben, gehört es doch zu den finstersten, die je niedergeschrieben wurden. Durch die Anrufung unheiliger Dämonen wird die Kraft der lebenden Toten verstärkt und sie verwandeln sich in untote Monster großer Kraft."
@@ -4154,7 +4024,7 @@ msgstr "Kein Magiegebiet"
msgctxt "spell"
msgid "summonfireelemental"
-msgstr "Beschwörung eines Hitzeelementar"
+msgstr "Hitzeelementar"
msgid "seed"
msgstr "Same"
@@ -4936,6 +4806,10 @@ msgctxt "keyword"
msgid "promote"
msgstr "BEFÖRDERE"
+msgctxt "keyword"
+msgid "locale"
+msgstr "SPRACHE"
+
msgid "skeleton_prefix_0"
msgstr "Klapperige"
@@ -6121,10 +5995,6 @@ msgctxt "race"
msgid "halfling_p"
msgstr "Halblinge"
-msgctxt "race"
-msgid "greenscarab"
-msgstr "grüner Scarabäus"
-
msgctxt "keyword"
msgid "quit"
msgstr "STIRB"
diff --git a/res/translations/strings.en.po b/res/translations/strings.en.po
index f1e04959e..f66058624 100644
--- a/res/translations/strings.en.po
+++ b/res/translations/strings.en.po
@@ -131,10 +131,6 @@ msgstr "POTIONS"
msgid "northwest"
msgstr "northwest"
-msgctxt "race"
-msgid "undeadpharaoh_d"
-msgstr "undead Pharaoh"
-
msgid "h19_p"
msgstr "white hemlocks"
@@ -162,14 +158,6 @@ msgstr "The famous bard Mirim was known for exceptionally limber play of the har
msgid "rustysword_p"
msgstr "rusty swords"
-msgctxt "race"
-msgid "apophis_d"
-msgstr "apophis"
-
-msgctxt "race"
-msgid "undeadpharaoh_p"
-msgstr "undead Pharaohs"
-
msgctxt "iteminfo"
msgid "magicbag"
msgstr "This bag encloses a dimensional fold, which can store up to 200 stones of weight without any extra burden on the bearer. Large items such as horses or carts cannot be placed inside."
@@ -200,10 +188,6 @@ msgctxt "calendar"
msgid "age_3"
msgstr "the third age"
-msgctxt "race"
-msgid "undeadpharaoh_x"
-msgstr "undead Pharaoh"
-
msgctxt "race"
msgid "youngdragon_d"
msgstr "young dragons"
@@ -223,10 +207,6 @@ msgctxt "spellinfo"
msgid "auraleak"
msgstr "With this dark ritual the chaos sorcerer causes a deep rift to appear in the astral balance that will tear all magical power from a region. All spellcasters in that region will lose most of their aura."
-msgctxt "race"
-msgid "apophis_p"
-msgstr "apophis"
-
msgctxt "spell"
msgid "calm_monster"
msgstr "Calm Monster"
@@ -239,10 +219,6 @@ msgctxt "spell"
msgid "readmind"
msgstr "Mind Probe"
-msgctxt "race"
-msgid "apophis_x"
-msgstr "apophis"
-
msgid "nr_options"
msgstr "Options"
@@ -537,10 +513,6 @@ msgstr "waterfinders"
msgid "tree"
msgstr "tree"
-msgctxt "race"
-msgid "redscarab"
-msgstr "red scarab"
-
msgctxt "race"
msgid "wolf_d"
msgstr "wolves"
@@ -665,10 +637,6 @@ msgctxt "race"
msgid "shadowmaster_d"
msgstr "shadowmasters"
-msgctxt "race"
-msgid "redscarab_d"
-msgstr "red scarab"
-
msgid "aurapotion50_p"
msgstr "aura potions"
@@ -691,9 +659,6 @@ msgctxt "spellinfo"
msgid "disturbingdreams"
msgstr "This spell causes insomnia and restlessness in a whole region for several weeks. All affected persons will learn much slower than normal."
-msgid "wdw_pyramid"
-msgstr "pyramid"
-
msgid "plain"
msgstr "plain"
@@ -704,10 +669,6 @@ msgstr "shadowmaster"
msgid "unarmed"
msgstr "unarmed"
-msgctxt "race"
-msgid "redscarab_p"
-msgstr "red scarabs"
-
msgctxt "race"
msgid "seaserpent_p"
msgstr "sea serpents"
@@ -740,10 +701,6 @@ msgstr "adamantium"
msgid "seashell"
msgstr "seashell"
-msgctxt "race"
-msgid "redscarab_x"
-msgstr "red scarab"
-
msgid "adamantiumplate"
msgstr "adamantium plate"
@@ -776,15 +733,11 @@ msgstr "Astral Disruption"
msgctxt "spell"
msgid "migration"
-msgstr "Rit of Acceptance"
+msgstr "Rite of Acceptance"
msgid "SCHIFF"
msgstr "SHIP"
-msgctxt "race"
-msgid "littlescarab"
-msgstr "little scarab"
-
msgid "spice_p"
msgstr "spice"
@@ -885,6 +838,12 @@ msgstr "a boat"
msgid "boat_p"
msgstr "boats"
+msgid "ship"
+msgstr "ship"
+
+msgid "ship_p"
+msgstr "ships"
+
msgctxt "race"
msgid "orc_p"
msgstr "orcs"
@@ -1388,10 +1347,6 @@ msgstr "wood"
msgid "presspass_p"
msgstr "presspasses"
-msgctxt "race"
-msgid "bluescarab"
-msgstr "blue scarab"
-
msgctxt "school"
msgid "draig"
msgstr "Draig"
@@ -1412,10 +1367,6 @@ msgctxt "skill"
msgid "taxation"
msgstr "taxation"
-msgctxt "race"
-msgid "undeadpharaoh"
-msgstr "undead Pharaoh"
-
msgctxt "spell"
msgid "heroic_song"
msgstr "Epic Heroes"
@@ -1424,10 +1375,6 @@ msgctxt "spell"
msgid "mallorntreegrow"
msgstr "Bless Mallorn Logs"
-msgctxt "race"
-msgid "littlescarab_d"
-msgstr "little scarab"
-
msgctxt "keyword"
msgid "hide"
msgstr "HIDE"
@@ -1447,10 +1394,6 @@ msgctxt "damage"
msgid "plusstrong"
msgstr "super strong"
-msgctxt "race"
-msgid "littlescarab_p"
-msgstr "little scarab"
-
msgid "an_unknown_ship"
msgstr "an unknown ship"
@@ -1496,10 +1439,6 @@ msgctxt "spellinfo"
msgid "rustweapon"
msgstr "This ritual conjurs up a dark thunderstorm that affects a whole region. The magic rain will let rust any ore. Iron weapons and armor will get rusty. The exact number of items affected by the rain depends on the ammount of power invested by the magician. Up to ten weapons can be destroyed per level - a Ring of Power increases the effect like an additional level."
-msgctxt "race"
-msgid "littlescarab_x"
-msgstr "little scarab"
-
msgid "rop"
msgstr "ring of power"
@@ -1924,10 +1863,6 @@ msgctxt "spell"
msgid "wdwpyramid_illaun"
msgstr "Dream of the gods"
-msgctxt "race"
-msgid "bluescarab_d"
-msgstr "blue scarab"
-
msgctxt "race"
msgid "snotling"
msgstr "snotling"
@@ -1962,10 +1897,6 @@ msgstr "gully "
msgid "citadel"
msgstr "citadel"
-msgctxt "race"
-msgid "bluescarab_p"
-msgstr "blue scarabs"
-
msgctxt "spell"
msgid "puttorest"
msgstr "Eternal Rest"
@@ -1986,10 +1917,6 @@ msgstr "wyrms"
msgid "viele"
msgstr "many"
-msgctxt "race"
-msgid "bluescarab_x"
-msgstr "blue scarab"
-
msgctxt "spell"
msgid "wdwpyramid_draig"
msgstr "Power of the Gods"
@@ -2551,7 +2478,7 @@ msgstr "Allow a Tangy Temerity to simmer for three hours in a litre of water, th
msgctxt "spell"
msgid "drought"
-msgstr "Summon Fire Elemental"
+msgstr "Gate to the elemental plane of fire"
msgid "pegasus_p"
msgstr "pegasi"
@@ -3349,10 +3276,6 @@ msgstr "silver"
msgid "jadee_ring_p"
msgstr "Jadee's wedding rings"
-msgctxt "race"
-msgid "apophis"
-msgstr "apophis"
-
msgctxt "shipinfo"
msgid "no_info"
msgstr "No Information available for this type of ship."
@@ -3614,10 +3537,6 @@ msgstr "New Spells"
msgid "nr_borderlist_prefix"
msgstr "To the "
-msgctxt "race"
-msgid "greenscarab_d"
-msgstr "green scarab"
-
msgctxt "iteminfo"
msgid "speedsail"
msgstr "A unit setting this sail on a ship temporarily will permanently increase the ship's range by 1."
@@ -3630,10 +3549,6 @@ msgctxt "spell"
msgid "flee"
msgstr "Unspeakable Horrors"
-msgctxt "race"
-msgid "greenscarab_p"
-msgstr "green scarab"
-
msgid "studypotion_p"
msgstr "brain boosts"
@@ -3661,10 +3576,6 @@ msgctxt "race"
msgid "human_d"
msgstr "humans"
-msgctxt "race"
-msgid "greenscarab_x"
-msgstr "green scarab"
-
msgctxt "spellinfo"
msgid "unholypower"
msgstr "Only whispered the knowledge of performing this ritual is passed to the adepts of the dark academies, for it is one of the darkest that has ever been written down. By calling unholy demons the strength of the living dead is greatly increased and they are turned into undead monsters of immense power."
@@ -4393,6 +4304,10 @@ msgctxt "keyword"
msgid "promote"
msgstr "PROMOTE"
+msgctxt "keyword"
+msgid "locale"
+msgstr "LANGUAGE"
+
msgid "stone"
msgstr "stone"
@@ -5421,10 +5336,6 @@ msgctxt "keyword"
msgid "quit"
msgstr "QUIT"
-msgctxt "race"
-msgid "greenscarab"
-msgstr "green scarab"
-
msgid "AURA"
msgstr "AURA"
diff --git a/s/cmake-init b/s/cmake-init
index 00e6d3d06..bdb5fce41 100755
--- a/s/cmake-init
+++ b/s/cmake-init
@@ -127,7 +127,6 @@ TOLUA
else
echo "tolua is $path"
fi
-luarocks --local install lunitx
unset path
set -e
diff --git a/s/preview b/s/preview
index 2fde22cda..717737a02 100755
--- a/s/preview
+++ b/s/preview
@@ -25,7 +25,7 @@ cd $SOURCE
rm -rf tolua
git fetch || abort "failed to update source. do you have local changes?"
[ -z $1 ] || git checkout $1
-git pull -q
+git pull --rebase -q
git submodule update
s/cmake-init
s/build > /dev/null || abort "build failed."
diff --git a/s/pull b/s/pull
deleted file mode 100755
index fa3167c5b..000000000
--- a/s/pull
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/sh
-if [ ! -z $1 ]; then
- git checkout $1
-fi
-git pull && git submodule update
diff --git a/s/upgrade b/s/upgrade
index 9d9d5a313..e7b91196b 100755
--- a/s/upgrade
+++ b/s/upgrade
@@ -7,7 +7,7 @@ while [ ! -d $ROOT/.git ]; do
done
cd $ROOT
-git pull
+git pull --rebase
git submodule update
s/build
s/runtests
diff --git a/scripts/eressea/equipment.lua b/scripts/eressea/equipment.lua
index 3163b85d9..328f33048 100644
--- a/scripts/eressea/equipment.lua
+++ b/scripts/eressea/equipment.lua
@@ -4,6 +4,15 @@ local self = {}
local function equip_first(u)
name = 'seed_' .. u.race
equip_unit(u, name, 255)
+ local r = u.region
+ r.terrain = 'plain'
+ r:set_flag(1, false) -- kein mallorn
+ r:set_resource('tree', 500)
+ r:set_resource('sapling', 100)
+ r:set_resource('seed', 50)
+ r:set_resource('peasant', 4000)
+ r:set_resource('money', 80000)
+ r:set_resource('horse', 50)
end
local mysets = {
diff --git a/scripts/tests/e2/e2features.lua b/scripts/tests/e2/e2features.lua
index f0ee62414..356e496e7 100644
--- a/scripts/tests/e2/e2features.lua
+++ b/scripts/tests/e2/e2features.lua
@@ -582,3 +582,40 @@ function test_buy_sell()
assert_equal(4, u:get_item(item))
assert_not_equal(0, u:get_item('money'))
end
+
+function test_seacast()
+ local r = region.create(0,0, "plain")
+ for i = 1,10 do
+ -- this prevents storms (only high seas have storms)
+ region.create(i, 1, "plain")
+ end
+ for i = 1,10 do
+ region.create(i, 0, "ocean")
+ end
+ local f = faction.create("human", "noreply@eressea.de", "de")
+ local s1 = ship.create(r, "boat")
+ local u1 = unit.create(f, r, 2)
+ u1:set_skill("sailing", 3)
+ u1:add_item("money", 1000)
+ u1.ship = s1
+ local u2 = unit.create(f, r, 1)
+ u2.race = "elf"
+ u2:set_skill("magic", 6)
+ u2.magic = "gwyrrd"
+ u2.aura = 60
+ u2.ship = s1
+ u2:add_spell("stormwinds")
+ u2:clear_orders()
+ u2:add_order("Zaubere stufe 2 'Sturmelementar' " .. itoa36(s1.id))
+ u1:clear_orders()
+ u1:add_order("NACH O O O O")
+ process_orders()
+ assert_equal(4, u2.region.x)
+
+ u2:clear_orders()
+ u2:add_order("Zaubere stufe 2 'Beschwörung eines Sturmelementares' " .. itoa36(s1.id))
+ u1:clear_orders()
+ u1:add_order("NACH O O O O")
+ process_orders()
+ assert_equal(8, u2.region.x)
+end
diff --git a/scripts/tests/e3/rules.lua b/scripts/tests/e3/rules.lua
index 89a6d822a..e39bd26c4 100644
--- a/scripts/tests/e3/rules.lua
+++ b/scripts/tests/e3/rules.lua
@@ -168,7 +168,7 @@ function test_no_teach()
-- TODO: assert something (reflecting skills sucks!)
end
-function test_seecast()
+function test_seacast()
local r = region.create(0,0, "plain")
for i = 1,10 do
-- this prevents storms (only high seas have storms)
@@ -192,14 +192,14 @@ function test_seecast()
u2:add_spell("stormwinds")
update_owners()
u2:clear_orders()
- u2:add_order("Zaubere stufe 2 'Beschwoere einen Sturmelementar' " .. itoa36(s1.id))
+ u2:add_order("Zaubere stufe 2 Sturmelementar " .. itoa36(s1.id))
u1:clear_orders()
u1:add_order("NACH O O O O")
process_orders()
assert_equal(4, u2.region.x)
u2:clear_orders()
- u2:add_order("Zaubere stufe 2 'Beschwoere einen Sturmelementar' " .. itoa36(s1.id))
+ u2:add_order("Zaubere stufe 2 Sturmelementar " .. itoa36(s1.id))
u1:clear_orders()
u1:add_order("NACH O O O O")
process_orders()
diff --git a/scripts/tests/economy.lua b/scripts/tests/economy.lua
index 271d245d6..1ed232502 100644
--- a/scripts/tests/economy.lua
+++ b/scripts/tests/economy.lua
@@ -214,6 +214,7 @@ function test_ironkeeper_guards_iron()
local level = r:get_resourcelevel("iron")
local u = unit.create(faction.create("human"), r)
u:set_skill("mining", level)
+ u:set_skill("stealth", 1) -- does not help against ironkeeper
local guard = unit.create(faction.create("mountainguard"), r, 1, "mountainguard")
guard:add_order("BEWACHEN")
u:add_order("MACHE EISEN")
@@ -223,6 +224,23 @@ function test_ironkeeper_guards_iron()
assert_equal(level, u:get_item("iron"))
end
+-- bug 2679
+function test_stealthy_iron_producer()
+ local r = region.create(0, 0, "plain")
+ r:set_resource("iron", 100)
+ local level = r:get_resourcelevel("iron")
+ local u = unit.create(faction.create("human"), r)
+ u:set_skill("stealth", 1)
+ u:set_skill("mining", level)
+ local guard = unit.create(faction.create("human"), r, 1, "human")
+ guard:add_order("BEWACHEN")
+ u:add_order("MACHE EISEN")
+ process_orders()
+ assert_equal(level, u:get_item("iron"))
+ process_orders()
+ assert_equal(2 * level, u:get_item("iron"))
+end
+
function test_sawmill()
local r = region.create(0, 0, "plain")
r:set_resource("tree", 100)
diff --git a/src/attributes/attributes.c b/src/attributes/attributes.c
index 4b1af76b0..54da3c6fd 100644
--- a/src/attributes/attributes.c
+++ b/src/attributes/attributes.c
@@ -121,7 +121,7 @@ void set_observer(region *r, faction *f, int skill, int turns)
attrib *a = a_find(r->attribs, &at_observer);
while (a && a->type == &at_observer) {
obs_data *od = (obs_data *)a->data.v;
- if (od->f == f && od->skill < skill) {
+ if (od->f == f) {
od->skill = skill;
od->timer = turns;
return;
diff --git a/src/battle.c b/src/battle.c
index 8c1627d97..4cd8e41cd 100644
--- a/src/battle.c
+++ b/src/battle.c
@@ -1123,26 +1123,23 @@ static bool survives(fighter *af, troop dt, battle *b) {
}
static void destroy_items(troop dt) {
- unit *du = dt.fighter->unit;
+ unit *du = dt.fighter->unit;
- item **pitm;
+ item **pitm;
- for (pitm = &du->items; *pitm;) {
- item *itm = *pitm;
- const item_type *itype = itm->type;
- if (!(itype->flags & ITF_CURSED) && dt.index < itm->number) {
- /* 25% Grundchance, das ein Item kaputtgeht. */
- if (rng_int() % 4 < 1) {
- i_change(pitm, itype, -1);
- }
- if (*pitm == itm) {
- pitm = &itm->next;
- }
- }
- else {
- pitm = &itm->next;
- }
- }
+ for (pitm = &du->items; *pitm;) {
+ item *itm = *pitm;
+ const item_type *itype = itm->type;
+ if (!(itype->flags & (ITF_CURSED | ITF_NOTLOST)) && dt.index < itm->number) {
+ /* 25% Grundchance, dass ein Item kaputtgeht. */
+ if (rng_int() % 4 < 1) {
+ i_change(pitm, itype, -1);
+ }
+ }
+ if (*pitm == itm) {
+ pitm = &itm->next;
+ }
+ }
}
static void calculate_defense_type(troop at, troop dt, int type, bool missile,
@@ -1741,7 +1738,7 @@ void do_combatmagic(battle * b, combatmagic_t was)
if (sp == NULL)
continue;
- ord = create_order(K_CAST, lang, "'%s'", spell_name(sp, lang));
+ ord = create_order(K_CAST, lang, "'%s'", spell_name(mkname_spell(sp), lang));
if (!cancast(mage, sp, 1, 1, ord)) {
free_order(ord);
continue;
@@ -1823,7 +1820,7 @@ static void do_combatspell(troop at)
fi->magic = 0; /* Hat keinen Kampfzauber, kaempft nichtmagisch weiter */
return;
}
- ord = create_order(K_CAST, lang, "'%s'", spell_name(sp, lang));
+ ord = create_order(K_CAST, lang, "'%s'", spell_name(mkname_spell(sp), lang));
if (!cancast(mage, sp, 1, 1, ord)) {
fi->magic = 0; /* Kann nicht mehr Zaubern, kaempft nichtmagisch weiter */
return;
diff --git a/src/bind_building.c b/src/bind_building.c
index 93576ed3b..d51122b0a 100644
--- a/src/bind_building.c
+++ b/src/bind_building.c
@@ -183,16 +183,20 @@ static int tolua_building_set_owner(lua_State * L)
static int tolua_building_create(lua_State * L)
{
- region *r = (region *)tolua_tousertype(L, 1, 0);
- const char *bname = tolua_tostring(L, 2, 0);
+ region *r = (region *)tolua_tousertype(L, 1, NULL);
+ const char *bname = tolua_tostring(L, 2, NULL);
+ int size = (int)tolua_tonumber(L, 3, 1);
if (!r) {
log_error("building.create expects a region as argument 1");
} else if (!bname) {
log_error("building.create expects a name as argument 2");
+ }
+ else if (size <= 0) {
+ log_error("building.create expects a size > 0");
} else {
const building_type *btype = bt_find(bname);
if (btype) {
- building *b = new_building(btype, r, default_locale);
+ building *b = new_building(btype, r, default_locale, size);
tolua_pushusertype(L, (void *)b, TOLUA_CAST "building");
return 1;
}
diff --git a/src/chaos.c b/src/chaos.c
index 69b9fa95d..5de776519 100644
--- a/src/chaos.c
+++ b/src/chaos.c
@@ -84,8 +84,9 @@ static void chaos(region * r, faction *monsters)
if (!(r->terrain->flags & SEA_REGION)) {
unit *u = random_unit(r);
if (u && !undeadrace(u_race(u))) {
+ faction * f = u->faction;
if (join_monsters(u, monsters)) {
- ADDMSG(&u->faction->msgs, msg_message("chaos_disease", "unit", u));
+ ADDMSG(&f->msgs, msg_message("chaos_disease", "unit", u));
u_setrace(u, get_race(RC_GHOUL));
}
}
diff --git a/src/creport.c b/src/creport.c
index 4f92fe132..18957447b 100644
--- a/src/creport.c
+++ b/src/creport.c
@@ -476,10 +476,12 @@ static int cr_spell(variant var, char *buffer, const void *userdata)
{
const faction *report = (const faction *)userdata;
spell *sp = (spell *)var.v;
- if (sp != NULL)
- sprintf(buffer, "\"%s\"", spell_name(sp, report->locale));
- else
+ if (sp != NULL) {
+ sprintf(buffer, "\"%s\"", spell_name(mkname_spell(sp), report->locale));
+ }
+ else {
strcpy(buffer, "\"\"");
+ }
return 0;
}
@@ -708,7 +710,8 @@ static void cr_output_spells(stream *out, const unit * u, int maxlevel)
spellbook_entry * sbe = (spellbook_entry *)selist_get(ql, qi);
if (sbe->level <= maxlevel) {
const spell *sp = spellref_get(&sbe->spref);
- const char *name = translate(mkname("spell", sp->sname), spell_name(sp, f->locale));
+ const char * spname = mkname("spell", sp->sname);
+ const char *name = translate(spname, spell_name(spname, f->locale));
if (!header) {
stream_printf(out, "SPRUECHE\n");
header = 1;
@@ -742,9 +745,6 @@ void cr_output_unit(stream *out, const faction * f,
assert(u && u->number);
- if (fval(u_race(u), RCF_INVISIBLE))
- return;
-
stream_printf(out, "EINHEIT %d\n", u->no);
stream_printf(out, "\"%s\";Name\n", unit_getname(u));
str = u_description(u, lang);
@@ -923,12 +923,12 @@ void cr_output_unit(stream *out, const faction * f,
int level;
const spell *sp = mage_get_combatspell(mage, i, &level);
if (sp) {
- const char *name;
+ const char *name = mkname_spell(sp);
if (level > maxlevel) {
level = maxlevel;
}
stream_printf(out, "KAMPFZAUBER %d\n", i);
- name = translate(mkname("spell", sp->sname), spell_name(sp, lang));
+ name = translate(name, spell_name(name, lang));
stream_printf(out, "\"%s\";name\n", name);
stream_printf(out, "%d;level\n", level);
}
@@ -1057,8 +1057,8 @@ static void cr_find_address(FILE * F, const faction * uf, selist * addresses)
static void cr_reportspell(FILE * F, const spell * sp, int level, const struct locale *lang)
{
int k;
- const char *name =
- translate(mkname("spell", sp->sname), spell_name(sp, lang));
+ const char *spname = mkname_spell(sp);
+ const char *name = translate(spname, spell_name(spname, lang));
fprintf(F, "ZAUBER %d\n", str_hash(sp->sname));
fprintf(F, "\"%s\";name\n", name);
@@ -1132,10 +1132,6 @@ cr_borders(const region * r, const faction * f, seen_mode mode, FILE * F)
const connection *b;
if (!r2)
continue;
- if (mode == seen_neighbour) {
- if (r2->seen.mode <= seen_neighbour)
- continue;
- }
b = get_borders(r, r2);
while (b) {
bool cs = b->type->fvisible(b, f, r);
diff --git a/src/economy.c b/src/economy.c
index 4d2c30d90..5f3bb8cbf 100644
--- a/src/economy.c
+++ b/src/economy.c
@@ -574,6 +574,12 @@ static void allocate_resource(unit * u, const resource_type * rtype, int want)
int amount, skill, skill_mod = 0;
variant save_mod;
skill_t sk;
+ static const struct race *rc_mountainguard;
+ static int config;
+
+ if (rc_changed(&config)) {
+ rc_mountainguard = rc_find("mountainguard");
+ }
/* momentan kann man keine ressourcen abbauen, wenn man dafuer
* Materialverbrauch hat: */
@@ -605,14 +611,16 @@ static void allocate_resource(unit * u, const resource_type * rtype, int want)
* Als magische Wesen 'sehen' Bergwaechter alles und werden durch
* Belagerung nicht aufgehalten. (Ansonsten wie oben bei Elfen anpassen).
*/
- if (itype->rtype && (itype->rtype == get_resourcetype(R_IRON) || itype->rtype == rt_find("laen"))) {
- unit *u2;
- for (u2 = r->units; u2; u2 = u2->next) {
- if (!fval(u2, UFL_ISNEW) && u2->number
- && is_guard(u2) && !alliedunit(u2, u->faction, HELP_GUARD)) {
- ADDMSG(&u->faction->msgs,
- msg_feedback(u, u->thisorder, "region_guarded", "guard", u2));
- return;
+ if (rc_mountainguard) {
+ if (itype->rtype && (itype->rtype == get_resourcetype(R_IRON) || itype->rtype == rt_find("laen"))) {
+ unit *u2;
+ for (u2 = r->units; u2; u2 = u2->next) {
+ if (rc_mountainguard == u_race(u2) && !fval(u2, UFL_ISNEW) && u2->number > 0
+ && is_guard(u2) && !alliedunit(u2, u->faction, HELP_GUARD)) {
+ ADDMSG(&u->faction->msgs,
+ msg_feedback(u, u->thisorder, "region_guarded", "guard", u2));
+ return;
+ }
}
}
}
@@ -1175,11 +1183,14 @@ bool trade_needs_castle(const terrain_type *terrain, const race *rc)
if (rc_changed(&rc_change)) {
rc_insect = get_race(RC_INSECT);
}
+ if (rc != rc_insect) {
+ return true;
+ }
if (terrain_changed(&terrain_change)) {
t_swamp = newterrain(T_SWAMP);
t_desert = newterrain(T_DESERT);
}
- return rc != rc_insect && (terrain == t_swamp || terrain == t_desert);
+ return (terrain != t_swamp && terrain != t_desert);
}
static building * first_building(region *r, const struct building_type *btype, int minsize) {
diff --git a/src/economy.test.c b/src/economy.test.c
index 034a6cf44..4cda069d0 100644
--- a/src/economy.test.c
+++ b/src/economy.test.c
@@ -269,7 +269,7 @@ static void test_trade_needs_castle(CuTest *tc) {
region *r;
unit *u;
building *b;
- const terrain_type *t_swamp;
+ const terrain_type *t_swamp, *t_desert, *t_plain;
const item_type *it_luxury;
test_setup();
@@ -278,11 +278,15 @@ static void test_trade_needs_castle(CuTest *tc) {
setup_terrains(tc);
init_terrains();
t_swamp = get_terrain("swamp");
- r = setup_trade_region(tc, t_swamp);
+ t_desert = get_terrain("desert");
+ t_plain = get_terrain("plain");
+ r = setup_trade_region(tc, t_plain);
it_luxury = r_luxury(r);
rc = test_create_race(NULL);
CuAssertTrue(tc, trade_needs_castle(t_swamp, rc));
+ CuAssertTrue(tc, trade_needs_castle(t_desert, rc));
+ CuAssertTrue(tc, trade_needs_castle(t_plain, rc));
u = test_create_unit(test_create_faction(rc), r);
unit_addorder(u, create_order(K_BUY, u->faction->locale, "1 %s",
@@ -305,6 +309,11 @@ static void test_trade_needs_castle(CuTest *tc) {
test_clear_messages(u->faction);
produce(r);
CuAssertIntEquals(tc, 0, test_count_messagetype(u->faction->msgs, "error119"));
+
+ rc = test_create_race("insect");
+ CuAssertTrue(tc, !trade_needs_castle(t_swamp, rc));
+ CuAssertTrue(tc, !trade_needs_castle(t_desert, rc));
+ CuAssertTrue(tc, trade_needs_castle(t_plain, rc));
test_teardown();
}
diff --git a/src/eressea.c b/src/eressea.c
index 6efb1cb0b..f40a7f994 100644
--- a/src/eressea.c
+++ b/src/eressea.c
@@ -10,6 +10,7 @@
#include "kernel/faction.h"
#include "kernel/item.h"
+#include "util/aliases.h"
#include "util/functions.h"
#include "util/language.h"
#include "util/log.h"
@@ -53,6 +54,7 @@ void game_done(void)
free_config();
free_locales();
#endif
+ free_aliases();
free_prefixes();
free_special_directions();
kernel_done();
diff --git a/src/give.c b/src/give.c
index e1a3cec66..0338fa590 100644
--- a/src/give.c
+++ b/src/give.c
@@ -302,7 +302,12 @@ static void transfer_ships(ship *s1, ship *s2, int n)
if (s1->coast != NODIRECTION) {
s2->coast = s1->coast;
}
- scale_ship(s1, s1->number - n);
+ if (n == s1->number) {
+ s1->number = 0;
+ }
+ else {
+ scale_ship(s1, s1->number - n);
+ }
}
static void transfer_units(ship *s1, ship *s2)
@@ -831,6 +836,16 @@ void give_cmd(unit * u, order * ord)
/* handled in give_control_cmd */
return;
}
+ else if (p == P_SHIP) {
+ ADDMSG(&u->faction->msgs,
+ msg_feedback(u, ord, "give_number_missing", "resource", "ship_p"));
+ return;
+ }
+ else if (p == P_PERSON) {
+ ADDMSG(&u->faction->msgs,
+ msg_feedback(u, ord, "give_number_missing", "resource", "person_p"));
+ return;
+ }
if (err == GET_NOTFOUND || (err != GET_PEASANTS && !can_give_to(u, u2))) {
ADDMSG(&u->faction->msgs,
diff --git a/src/gmtool.c b/src/gmtool.c
index 394f1efde..033823dc0 100644
--- a/src/gmtool.c
+++ b/src/gmtool.c
@@ -1512,17 +1512,6 @@ static void handlekey(state * st, int c)
break;
}
}
- if (wnd == NULL) {
- static char kbuffer[80];
- if (kbuffer[0] == 0) {
- strcpy(kbuffer, "getch:");
- }
- sprintf(sbuffer, " 0x%x", c);
- strncat(kbuffer, sbuffer, sizeof(kbuffer) - 1);
- statusline(st->wnd_status->handle, kbuffer);
- if (strlen(kbuffer) > 70)
- kbuffer[0] = 0;
- }
break;
}
}
diff --git a/src/jsonconf.c b/src/jsonconf.c
index 8761f36be..27a018b17 100644
--- a/src/jsonconf.c
+++ b/src/jsonconf.c
@@ -18,6 +18,7 @@
/* util includes */
#include "kernel/attrib.h"
+#include "util/aliases.h"
#include "util/crmessage.h"
#include "util/functions.h"
#include "util/keyword.h"
@@ -696,6 +697,41 @@ static void json_strings(cJSON *json) {
}
}
+static void json_add_aliases(cJSON *json, const struct locale *lang) {
+ cJSON *child;
+ str_aliases *aliases = get_aliases(lang);
+ for (child = json->child; child; child = child->next) {
+ if (child->type == cJSON_String) {
+ alias_add(aliases, child->string, child->valuestring);
+ }
+ else if (child->type == cJSON_Array) {
+ cJSON *entry;
+ for (entry = child->child; entry; entry = entry->next) {
+ if (entry->type == cJSON_String) {
+ alias_add(aliases, child->string, entry->valuestring);
+ }
+ }
+ }
+ }
+}
+
+static void json_aliases(cJSON *json) {
+ cJSON *child;
+ if (json->type != cJSON_Object) {
+ log_error("aliases is not a json array: %d", json->type);
+ return;
+ }
+ for (child = json->child; child; child = child->next) {
+ if (child->type == cJSON_Object) {
+ struct locale *lang = get_locale(child->string);
+ json_add_aliases(child, lang);
+ }
+ else {
+ log_error("strings for locale `%s` are not a json object: %d", child->string, child->type);
+ }
+ }
+}
+
static void json_direction(cJSON *json, struct locale *lang) {
cJSON *child;
if (json->type != cJSON_Object) {
@@ -1108,6 +1144,9 @@ void json_config(cJSON *json) {
else if (strcmp(child->string, "strings") == 0) {
json_strings(child);
}
+ else if (strcmp(child->string, "aliases") == 0) {
+ json_aliases(child);
+ }
else if (strcmp(child->string, "calendar") == 0) {
json_calendar(child);
}
diff --git a/src/kernel/build.c b/src/kernel/build.c
index 3eb9870de..76cbe9f34 100644
--- a/src/kernel/build.c
+++ b/src/kernel/build.c
@@ -827,8 +827,7 @@ build_building(unit * u, const building_type * btype, int id, int want, order *
b->size += built;
} else {
/* build a new building */
- b = new_building(btype, r, lang);
- b->size = built;
+ b = new_building(btype, r, lang, built);
b->type = btype;
fset(b, BLD_MAINTAINED);
diff --git a/src/kernel/building.c b/src/kernel/building.c
index a6167f880..c8c19cc9d 100644
--- a/src/kernel/building.c
+++ b/src/kernel/building.c
@@ -369,7 +369,7 @@ building *building_create(int id)
}
building *new_building(const struct building_type * btype, region * r,
- const struct locale * lang)
+ const struct locale * lang, int size)
{
building **bptr = &r->buildings;
int id = newcontainerid();
@@ -377,6 +377,7 @@ building *new_building(const struct building_type * btype, region * r,
const char *bname;
char buffer[32];
+ assert(size > 0);
b->type = btype;
b->region = r;
while (*bptr)
@@ -396,6 +397,7 @@ building *new_building(const struct building_type * btype, region * r,
assert(bname);
snprintf(buffer, sizeof(buffer), "%s %s", bname, itoa36(b->no));
b->name = str_strdup(bname);
+ b->size = size;
return b;
}
diff --git a/src/kernel/building.h b/src/kernel/building.h
index 2471bbfdc..74128a036 100644
--- a/src/kernel/building.h
+++ b/src/kernel/building.h
@@ -111,7 +111,7 @@ extern "C" {
int buildingcapacity(const struct building *b);
struct building *building_create(int id);
struct building *new_building(const struct building_type *typ,
- struct region *r, const struct locale *lang);
+ struct region *r, const struct locale *lang, int size);
int build_building(struct unit *u, const struct building_type *typ,
int id, int size, struct order *ord);
bool building_finished(const struct building *b);
diff --git a/src/kernel/faction.c b/src/kernel/faction.c
index c17f4662c..03026a7bd 100755
--- a/src/kernel/faction.c
+++ b/src/kernel/faction.c
@@ -835,3 +835,18 @@ faction *faction_create(int no)
fhash(f);
return f;
}
+
+void change_locale(faction *f, const struct locale *lang) {
+ unit *ux;
+ for (ux = f->units; ux; ux = ux->nextF) {
+ translate_orders(ux, lang, &ux->orders);
+ if (ux->old_orders) {
+ translate_orders(ux, lang, &ux->old_orders);
+ }
+ if (ux->thisorder) {
+ translate_orders(ux, lang, &ux->thisorder);
+ }
+ }
+ f->locale = lang;
+}
+
diff --git a/src/kernel/faction.h b/src/kernel/faction.h
index 0fcb2ff4a..94835c33e 100644
--- a/src/kernel/faction.h
+++ b/src/kernel/faction.h
@@ -155,6 +155,7 @@ extern "C" {
int count_migrants(const struct faction * f);
int count_maxmigrants(const struct faction * f);
int max_magicians(const struct faction * f);
+ void change_locale(struct faction *f, const struct locale *lang);
#define MONSTER_ID 666
struct faction *getfaction(void);
diff --git a/src/kernel/faction.test.c b/src/kernel/faction.test.c
index 2b7d97a5a..21b9b5fad 100644
--- a/src/kernel/faction.test.c
+++ b/src/kernel/faction.test.c
@@ -135,11 +135,59 @@ static void test_addfaction(CuTest *tc) {
static void test_check_passwd(CuTest *tc) {
faction *f;
+ test_setup();
f = test_create_faction(NULL);
faction_setpassword(f, password_hash("password", PASSWORD_DEFAULT));
CuAssertTrue(tc, checkpasswd(f, "password"));
CuAssertTrue(tc, !checkpasswd(f, "assword"));
CuAssertTrue(tc, !checkpasswd(f, "PASSWORD"));
+ test_teardown();
+}
+
+static void test_change_locale(CuTest *tc) {
+ faction *f;
+ unit *u;
+ order *ord;
+ struct locale *lang;
+
+ test_setup();
+ f = test_create_faction(NULL);
+ lang = get_or_create_locale("en");
+ u = test_create_unit(f, test_create_plain(0, 0));
+ u->thisorder = create_order(K_ENTERTAIN, f->locale, NULL);
+
+ u->old_orders = create_order(K_WORK, f->locale, NULL);
+ u->old_orders->next = ord = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ALCHEMY]);
+ CuAssertIntEquals(tc, SK_ALCHEMY - 100, ord->id);
+ ord->next = create_order(K_GIVE, f->locale, "abcd 1 Schwert");
+
+ unit_addorder(u, create_order(K_NAME, f->locale, "EINHEIT Hodor"));
+ unit_addorder(u, create_order(K_ENTERTAIN, f->locale, NULL));
+ unit_addorder(u, create_order(K_KOMMENTAR, f->locale, "ich bin kein Tintenfisch"));
+ unit_addorder(u, ord = create_order(K_STUDY, f->locale, skillnames[SK_ENTERTAINMENT]));
+ CuAssertIntEquals(tc, SK_ENTERTAINMENT - 100, ord->id);
+
+ change_locale(f, lang);
+ CuAssertPtrEquals(tc, lang, (void *)f->locale);
+ CuAssertPtrNotNull(tc, u->thisorder);
+
+ CuAssertPtrNotNull(tc, ord = u->old_orders);
+ CuAssertIntEquals(tc, K_WORK, ord->command);
+ CuAssertPtrNotNull(tc, ord = ord->next);
+ CuAssertIntEquals(tc, K_AUTOSTUDY, ord->command);
+ CuAssertIntEquals(tc, SK_ALCHEMY - 100, ord->id);
+ CuAssertPtrEquals(tc, NULL, ord->next);
+
+ CuAssertPtrNotNull(tc, ord = u->orders);
+ CuAssertIntEquals(tc, K_ENTERTAIN, ord->command);
+ CuAssertPtrNotNull(tc, ord = ord->next);
+ CuAssertIntEquals(tc, K_KOMMENTAR, ord->command);
+ CuAssertPtrNotNull(tc, ord = ord->next);
+ CuAssertIntEquals(tc, K_STUDY, ord->command);
+ CuAssertIntEquals(tc, SK_ENTERTAINMENT - 100, ord->id);
+ CuAssertPtrEquals(tc, NULL, ord->next);
+
+ test_teardown();
}
static void test_get_monsters(CuTest *tc) {
@@ -366,6 +414,7 @@ CuSuite *get_faction_suite(void)
SUITE_ADD_TEST(suite, test_set_origin);
SUITE_ADD_TEST(suite, test_set_origin_bug);
SUITE_ADD_TEST(suite, test_check_passwd);
+ SUITE_ADD_TEST(suite, test_change_locale);
SUITE_ADD_TEST(suite, test_valid_race);
SUITE_ADD_TEST(suite, test_set_email);
SUITE_ADD_TEST(suite, test_dbstrings);
diff --git a/src/kernel/order.c b/src/kernel/order.c
index 056722294..96626bfc0 100644
--- a/src/kernel/order.c
+++ b/src/kernel/order.c
@@ -213,7 +213,7 @@ int stream_order(struct stream *out, const struct order *ord, const struct local
void free_order(order * ord)
{
if (ord != NULL) {
- assert(ord->next == 0);
+ assert(ord->next == NULL);
free(ord);
}
}
@@ -641,4 +641,3 @@ void close_orders(void) {
(void)init_order(NULL, NULL);
}
}
-
diff --git a/src/kernel/order.test.c b/src/kernel/order.test.c
index 14cdeb157..1ce993a69 100644
--- a/src/kernel/order.test.c
+++ b/src/kernel/order.test.c
@@ -103,7 +103,8 @@ static void test_parse_make(CuTest *tc) {
locale_setstring(lang, keyword(K_MAKE), "MAKE");
locale_setstring(lang, keyword(K_MAKETEMP), "MAKETEMP");
init_locale(lang);
- ord = parse_order("M hurrdurr", lang);
+ CuAssertPtrEquals(tc, NULL, parse_order("M herp", lang));
+ ord = parse_order("MA hurrdurr", lang);
CuAssertPtrNotNull(tc, ord);
CuAssertIntEquals(tc, K_MAKE, getkeyword(ord));
CuAssertStrEquals(tc, "MAKE hurrdurr", get_command(ord, lang, cmd, sizeof(cmd)));
@@ -163,7 +164,8 @@ static void test_parse_make_temp(CuTest *tc) {
locale_setstring(lang, parameters[P_TEMP], "TEMP");
init_locale(lang);
- ord = parse_order("M T herp", lang);
+ CuAssertPtrEquals(tc, NULL, parse_order("M T herp", lang));
+ ord = parse_order("MA TE herp", lang);
CuAssertPtrNotNull(tc, ord);
CuAssertIntEquals(tc, K_MAKETEMP, getkeyword(ord));
CuAssertStrEquals(tc, "MAKETEMP herp", get_command(ord, lang, cmd, sizeof(cmd)));
diff --git a/src/kernel/pathfinder.c b/src/kernel/pathfinder.c
index f2103f63f..15ac0e966 100644
--- a/src/kernel/pathfinder.c
+++ b/src/kernel/pathfinder.c
@@ -79,7 +79,7 @@ static void free_nodes(node * root)
}
}
-struct selist *regions_in_range(struct region *handle_start, int maxdist,
+struct selist *path_regions_in_range(struct region *handle_start, int maxdist,
bool(*allowed) (const struct region *, const struct region *))
{
selist * rlist = NULL;
diff --git a/src/kernel/pathfinder.h b/src/kernel/pathfinder.h
index 9557fe735..df4bfbe80 100644
--- a/src/kernel/pathfinder.h
+++ b/src/kernel/pathfinder.h
@@ -8,15 +8,10 @@ extern "C" {
const struct region *target, int maxlen,
bool(*allowed) (const struct region *, const struct region *));
extern bool path_exists(struct region *handle_start, const struct region *target,
- int maxlen, bool(*allowed) (const struct region *,
- const struct region *));
- extern bool allowed_swim(const struct region *src,
- const struct region *target);
- extern bool allowed_fly(const struct region *src,
- const struct region *target);
- extern bool allowed_walk(const struct region *src,
- const struct region *target);
- extern struct selist *regions_in_range(struct region *src, int maxdist,
+ int maxlen, bool(*allowed) (const struct region *, const struct region *));
+ extern bool allowed_fly(const struct region *src, const struct region *target);
+ extern bool allowed_walk(const struct region *src, const struct region *target);
+ extern struct selist *path_regions_in_range(struct region *src, int maxdist,
bool(*allowed) (const struct region *, const struct region *));
extern void pathfinder_cleanup(void);
diff --git a/src/kernel/race.h b/src/kernel/race.h
index 8918437e1..a034fd666 100644
--- a/src/kernel/race.h
+++ b/src/kernel/race.h
@@ -212,11 +212,10 @@ extern "C" {
#define RCF_COASTAL (1<<22) /* kann in Landregionen an der Kueste sein */
#define RCF_UNARMEDGUARD (1<<23) /* kann ohne Waffen bewachen */
#define RCF_CANSAIL (1<<24) /* Einheit darf Schiffe betreten */
-#define RCF_INVISIBLE (1<<25) /* not visible in any report */
+#define RCF_FAMILIAR (1<<25) /* may be a familiar */
#define RCF_SHIPSPEED (1<<26) /* race gets +1 on shipspeed */
#define RCF_ATTACK_MOVED (1<<27) /* may attack if it has moved */
#define RCF_MIGRANTS (1<<28) /* may have migrant units (human bonus) */
-#define RCF_FAMILIAR (1<<29) /* may be a familiar */
#define RCF_DEFAULT RCF_CANSAIL
diff --git a/src/kernel/region.c b/src/kernel/region.c
index b3bc9a0e9..07916fda0 100644
--- a/src/kernel/region.c
+++ b/src/kernel/region.c
@@ -1396,7 +1396,6 @@ void region_set_owner(struct region *r, struct faction *owner, int turn)
faction *update_owners(region * r)
{
faction *f = NULL;
- assert(rule_region_owners());
if (r->land) {
building *bowner = largestbuilding(r, cmp_current_owner, false);
building *blargest = largestbuilding(r, cmp_taxes, false);
diff --git a/src/kernel/save.c b/src/kernel/save.c
index 4d9574a61..c720441c0 100644
--- a/src/kernel/save.c
+++ b/src/kernel/save.c
@@ -43,7 +43,6 @@
#include
#include
#include
-#include
#include
#include
#include
@@ -1252,6 +1251,7 @@ void write_building(gamedata *data, const building *b)
write_building_reference(b, store);
WRITE_STR(store, b->name);
WRITE_STR(store, b->display ? b->display : "");
+ assert(b->size > 0);
WRITE_INT(store, b->size);
WRITE_TOK(store, b->type->_name);
write_attribs(store, b->attribs, b);
@@ -1277,6 +1277,10 @@ struct building *read_building(gamedata *data) {
}
b->display = str_strdup(name);
READ_INT(store, &b->size);
+ if (b->size < 1) {
+ log_warning("trim building %s had size %d", itoa36(b->no), b->size);
+ b->size = 1;
+ }
READ_STR(store, name, sizeof(name));
b->type = bt_find(name);
if (!b->type) {
diff --git a/src/kernel/ship.c b/src/kernel/ship.c
index 87194834b..1458953cb 100644
--- a/src/kernel/ship.c
+++ b/src/kernel/ship.c
@@ -146,12 +146,14 @@ void sunhash(ship * s)
static ship *sfindhash(int i)
{
- ship *old;
+ ship *sh;
- for (old = shiphash[i % MAXSHIPHASH]; old; old = old->nexthash)
- if (old->no == i)
- return old;
- return 0;
+ for (sh = shiphash[i % MAXSHIPHASH]; sh; sh = sh->nexthash) {
+ if (sh->no == i) {
+ return sh->number > 0 ? sh : NULL;
+ }
+ }
+ return NULL;
}
struct ship *findship(int i)
@@ -406,9 +408,12 @@ int crew_skill(const ship *sh) {
return n;
}
-bool ship_crewed(const ship *sh) {
- unit *u, *cap = ship_owner(sh);
+bool ship_crewed(const ship *sh, const unit *cap) {
+ unit *u;
int capskill = -1, sumskill = 0;
+ if (cap == NULL) {
+ return false;
+ }
for (u = sh->region->units; u; u = u->next) {
if (u->ship == sh) {
int es = effskill(u, SK_SAILING, NULL);
@@ -510,12 +515,15 @@ void ship_update_owner(ship * sh) {
unit *ship_owner(const ship * sh)
{
- unit *owner = sh->_owner;
- if (!owner || (owner->ship != sh || owner->number <= 0)) {
- unit * heir = ship_owner_ex(sh, owner ? owner->faction : 0);
- return (heir && heir->number > 0) ? heir : 0;
+ if (sh->number > 0) {
+ unit *owner = sh->_owner;
+ if (!owner || owner->ship != sh) {
+ unit * heir = ship_owner_ex(sh, owner ? owner->faction : NULL);
+ return (heir && heir->number > 0) ? heir : NULL;
+ }
+ return owner;
}
- return owner;
+ return NULL;
}
void write_ship_reference(const struct ship *sh, struct storage *store)
diff --git a/src/kernel/ship.h b/src/kernel/ship.h
index d21f9f3c3..ee45ce2c2 100644
--- a/src/kernel/ship.h
+++ b/src/kernel/ship.h
@@ -117,7 +117,7 @@ extern "C" {
void ship_setname(struct ship *self, const char *name);
int shipspeed(const struct ship *sh, const struct unit *u);
- bool ship_crewed(const struct ship *sh);
+ bool ship_crewed(const struct ship *sh, const struct unit *cap);
int crew_skill(const struct ship *sh);
int ship_captain_minskill(const struct ship *sh);
diff --git a/src/kernel/ship.test.c b/src/kernel/ship.test.c
index 174762182..9b11d6f72 100644
--- a/src/kernel/ship.test.c
+++ b/src/kernel/ship.test.c
@@ -45,39 +45,39 @@ static void test_ship_crewed(CuTest * tc)
stype->cptskill = 2;
stype->sumskill = 4;
sh = test_create_ship(r, stype);
- CuAssertTrue(tc, !ship_crewed(sh));
+ CuAssertTrue(tc, !ship_crewed(sh, NULL));
u1 = test_create_unit(f, r);
set_level(u1, SK_SAILING, 4);
u_set_ship(u1, sh);
- CuAssertTrue(tc, ship_crewed(sh));
+ CuAssertTrue(tc, ship_crewed(sh, u1));
u2 = test_create_unit(f, r);
set_level(u1, SK_SAILING, 2);
set_level(u2, SK_SAILING, 2);
u_set_ship(u2, sh);
- CuAssertTrue(tc, ship_crewed(sh));
+ CuAssertTrue(tc, ship_crewed(sh, u1));
set_level(u1, SK_SAILING, 1);
set_level(u2, SK_SAILING, 2);
- CuAssertTrue(tc, !ship_crewed(sh));
+ CuAssertTrue(tc, !ship_crewed(sh, u1));
set_level(u1, SK_SAILING, 2);
set_level(u2, SK_SAILING, 1);
- CuAssertTrue(tc, !ship_crewed(sh));
+ CuAssertTrue(tc, !ship_crewed(sh, u1));
set_level(u1, SK_SAILING, 3);
set_level(u2, SK_SAILING, 1);
- CuAssertTrue(tc, ship_crewed(sh));
+ CuAssertTrue(tc, ship_crewed(sh, u1));
stype->minskill = 2;
- CuAssertTrue(tc, !ship_crewed(sh));
+ CuAssertTrue(tc, !ship_crewed(sh, u1));
set_level(u1, SK_SAILING, 2);
set_level(u2, SK_SAILING, 2);
- CuAssertTrue(tc, ship_crewed(sh));
+ CuAssertTrue(tc, ship_crewed(sh, u1));
sh->number = 2;
- CuAssertTrue(tc, !ship_crewed(sh));
+ CuAssertTrue(tc, !ship_crewed(sh, u1));
set_level(u1, SK_SAILING, 4);
set_level(u2, SK_SAILING, 4);
- CuAssertTrue(tc, !ship_crewed(sh));
+ CuAssertTrue(tc, !ship_crewed(sh, u1));
u1->number = 2;
set_level(u1, SK_SAILING, 2);
set_level(u2, SK_SAILING, 4);
- CuAssertTrue(tc, ship_crewed(sh));
+ CuAssertTrue(tc, ship_crewed(sh, u1));
test_teardown();
}
@@ -142,7 +142,7 @@ static void test_shipowner_goes_to_next_when_empty(CuTest * tc)
u_set_ship(u, sh);
u_set_ship(u2, sh);
CuAssertPtrEquals(tc, u, ship_owner(sh));
- u->number = 0;
+ leave_ship(u);
CuAssertPtrEquals(tc, u2, ship_owner(sh));
test_teardown();
}
@@ -177,7 +177,7 @@ static void test_shipowner_goes_to_other_when_empty(CuTest * tc)
u_set_ship(u, sh);
u_set_ship(u2, sh);
CuAssertPtrEquals(tc, u, ship_owner(sh));
- u->number = 0;
+ leave_ship(u);
CuAssertPtrEquals(tc, u2, ship_owner(sh));
test_teardown();
}
@@ -215,9 +215,9 @@ static void test_shipowner_goes_to_same_faction_when_empty(CuTest * tc)
u_set_ship(u2, sh);
u_set_ship(u3, sh);
CuAssertPtrEquals(tc, u, ship_owner(sh));
- u->number = 0;
+ leave_ship(u);
CuAssertPtrEquals(tc, u3, ship_owner(sh));
- u3->number = 0;
+ leave_ship(u3);
CuAssertPtrEquals(tc, u2, ship_owner(sh));
test_teardown();
}
@@ -362,10 +362,8 @@ static void test_shipowner_resets_when_empty(CuTest * tc)
CuAssertPtrNotNull(tc, u);
u_set_ship(u, sh);
CuAssertPtrEquals(tc, u, ship_owner(sh));
- u->number = 0;
+ leave_ship(u);
CuAssertPtrEquals(tc, NULL, ship_owner(sh));
- u->number = 1;
- CuAssertPtrEquals(tc, u, ship_owner(sh));
test_teardown();
}
@@ -401,12 +399,12 @@ void test_shipowner_goes_to_empty_unit_after_leave(CuTest * tc)
u_set_ship(u3, sh);
CuAssertPtrEquals(tc, u1, ship_owner(sh));
- u2->number = 0;
+ leave_ship(u2);
leave_ship(u1);
CuAssertPtrEquals(tc, u3, ship_owner(sh));
leave_ship(u3);
CuAssertPtrEquals(tc, NULL, ship_owner(sh));
- u2->number = 1;
+ u2->ship = sh;
CuAssertPtrEquals(tc, u2, ship_owner(sh));
test_teardown();
}
diff --git a/src/kernel/unit.c b/src/kernel/unit.c
index 6b4cbe2b0..67a8f203b 100644
--- a/src/kernel/unit.c
+++ b/src/kernel/unit.c
@@ -937,9 +937,7 @@ void u_setfaction(unit * u, faction * f)
bool count_unit(const unit *u)
{
- const race *rc = u_race(u);
- /* spells are invisible. units we cannot see do not count to our limit */
- return rc == NULL || (rc->flags & RCF_INVISIBLE) == 0;
+ return u_race(u) != NULL;
}
void set_number(unit * u, int count)
@@ -948,7 +946,7 @@ void set_number(unit * u, int count)
assert(count <= UNIT_MAXSIZE);
if (count == 0) {
- u->flags &= ~(UFL_HERO);
+ u->flags &= ~UFL_HERO;
}
if (u->faction && count_unit(u)) {
u->faction->num_people += count - u->number;
@@ -974,6 +972,12 @@ void remove_skill(unit * u, skill_t sk)
}
}
+static void remove_skills(unit * u) {
+ free(u->skills);
+ u->skills = NULL;
+ u->skill_size = 0;
+}
+
skill *add_skill(unit * u, skill_t sk)
{
skill *sv;
@@ -1587,10 +1591,7 @@ void scale_number(unit * u, int n)
}
}
if (u->number == 0 || n == 0) {
- skill_t sk;
- for (sk = 0; sk < MAXSKILLS; sk++) {
- remove_skill(u, sk);
- }
+ remove_skills(u);
}
set_number(u, n);
@@ -1847,3 +1848,46 @@ void unit_convert_race(unit *u, const race *rc, const char *rcname)
}
}
+
+void translate_orders(unit *u, const struct locale *lang, order **list)
+{
+ order **po = list;
+ (void)lang;
+ while (*po) {
+ order *ord = *po;
+ if (ord->id <= 0) {
+ /* we can keep these, they have no problematic arguments */
+ po = &ord->next;
+ continue;
+ }
+ switch (getkeyword(ord)) {
+ case K_ATTACK:
+ case K_BANNER:
+ case K_DRIVE:
+ case K_FOLLOW:
+ case K_GROUP:
+ case K_KOMMENTAR:
+ case K_MAIL:
+ case K_NUMBER:
+ case K_PASSWORD:
+ case K_PREFIX:
+ case K_RECRUIT:
+ case K_SPY:
+ case K_STEAL:
+ case K_TEACH:
+ case K_TRANSPORT:
+ case K_URSPRUNG:
+ /* we can keep these, they need no translation */
+ po = &ord->next;
+ break;
+ default:
+ /* we don't know what to do with these, drop them */
+ if (u->thisorder == ord) {
+ u->thisorder = NULL;
+ }
+ *po = ord->next;
+ ord->next = NULL;
+ free_order(ord);
+ }
+ }
+}
diff --git a/src/kernel/unit.h b/src/kernel/unit.h
index 7fc6fa730..934527db1 100644
--- a/src/kernel/unit.h
+++ b/src/kernel/unit.h
@@ -223,6 +223,8 @@ extern "C" {
bool unit_name_equals_race(const struct unit *u);
void unit_convert_race(struct unit *u, const struct race *rc, const char *rcname);
+ void translate_orders(struct unit *u, const struct locale *lang, struct order **list);
+
/* getunit results: */
#define GET_UNIT 0
#define GET_NOTFOUND 1
diff --git a/src/kernel/unit.test.c b/src/kernel/unit.test.c
index a5b0ec89d..75fd9ea08 100644
--- a/src/kernel/unit.test.c
+++ b/src/kernel/unit.test.c
@@ -520,14 +520,11 @@ static void test_heal_factor(CuTest *tc) {
}
static void test_unlimited_units(CuTest *tc) {
- race *rc;
faction *f;
unit *u;
test_setup();
f = test_create_faction(NULL);
- rc = test_create_race("spell");
- rc->flags |= RCF_INVISIBLE;
CuAssertIntEquals(tc, 0, f->num_units);
CuAssertIntEquals(tc, 0, f->num_people);
u = test_create_unit(f, test_create_region(0, 0, NULL));
@@ -540,14 +537,7 @@ static void test_unlimited_units(CuTest *tc) {
u_setfaction(u, f);
CuAssertIntEquals(tc, 1, f->num_units);
CuAssertIntEquals(tc, 1, f->num_people);
- u_setrace(u, rc);
- CuAssertTrue(tc, !count_unit(u));
- CuAssertIntEquals(tc, 0, f->num_units);
- CuAssertIntEquals(tc, 0, f->num_people);
scale_number(u, 10);
- CuAssertIntEquals(tc, 0, f->num_units);
- CuAssertIntEquals(tc, 0, f->num_people);
- u_setrace(u, f->race);
CuAssertIntEquals(tc, 1, f->num_units);
CuAssertIntEquals(tc, 10, f->num_people);
remove_unit(&u->region->units, u);
diff --git a/src/kernel/version.c b/src/kernel/version.c
index 7546bceac..8c694f7f0 100644
--- a/src/kernel/version.c
+++ b/src/kernel/version.c
@@ -8,7 +8,7 @@
#ifndef ERESSEA_VERSION
/* the version number, if it was not passed to make with -D */
-#define ERESSEA_VERSION "3.24.0"
+#define ERESSEA_VERSION "3.25.0"
#endif
const char *eressea_version(void) {
diff --git a/src/laws.c b/src/laws.c
index 01aa4be8d..bfe13d665 100644
--- a/src/laws.c
+++ b/src/laws.c
@@ -681,8 +681,8 @@ growing_trees(region * r, const season_t current_season, const season_t last_wee
a = a_find(r->attribs, &at_germs);
if (!a) {
a = a_add(&r->attribs, a_new(&at_germs));
- a->data.sa[0] = (short)cap_int(rtrees(r, 0), 0, SHRT_MAX);
- a->data.sa[1] = (short)cap_int(rtrees(r, 1), 0, SHRT_MAX);
+ a->data.sa[0] = (short)cap_int((8 + rtrees(r, 0)) / 9, 0, SHRT_MAX);
+ a->data.sa[1] = (short)cap_int((8 + rtrees(r, 1)) / 9, 0, SHRT_MAX);
}
else if (a->data.sa[0] < 0 || a->data.sa[1] < 0) {
a->data.sa[0] = (short)cap_int(a->data.sa[0], 0, SHRT_MAX);
@@ -692,9 +692,7 @@ growing_trees(region * r, const season_t current_season, const season_t last_wee
/* Baumwachstum */
sprout = rtrees(r, 1);
if (sprout > a->data.sa[1]) sprout = a->data.sa[1];
- grownup_trees = sprout / 6;
- /* aus dem Sproesslingepool dieses Jahres abziehen */
- a->data.sa[1] = (short)(sprout - grownup_trees);
+ grownup_trees = sprout;
/* aus dem gesamt Sproesslingepool abziehen */
rsettrees(r, 1, rtrees(r, 1) - grownup_trees);
/* zu den Baeumen hinzufuegen */
@@ -703,13 +701,10 @@ growing_trees(region * r, const season_t current_season, const season_t last_wee
/* Samenwachstum */
seeds = rtrees(r, 0);
if (seeds > a->data.sa[0]) seeds = a->data.sa[0];
- sprout = seeds / 6;
- /* aus dem Samenpool dieses Jahres abziehen */
- a->data.sa[0] = (short)(seeds - sprout);
/* aus dem gesamt Samenpool abziehen */
- rsettrees(r, 0, rtrees(r, 0) - sprout);
+ rsettrees(r, 0, rtrees(r, 0) - seeds);
/* zu den Sproesslinge hinzufuegen */
- rsettrees(r, 1, rtrees(r, 1) + sprout);
+ rsettrees(r, 1, rtrees(r, 1) + seeds);
}
}
@@ -885,7 +880,7 @@ int leave_cmd(unit * u, struct order *ord)
}
if (fval(r->terrain, SEA_REGION) && u->ship) {
- if (!fval(u_race(u), RCF_SWIM)) {
+ if (!fval(u_race(u), RCF_SWIM|RCF_FLY)) {
cmistake(u, ord, 11, MSG_MOVE);
return 0;
}
@@ -2576,22 +2571,28 @@ void sinkships(struct region * r)
while (*shp) {
ship *sh = *shp;
- if (!sh->type->construction || sh->size >= sh->type->construction->maxsize) {
- if (fval(r->terrain, SEA_REGION)) {
- if (!ship_crewed(sh)) {
- /* ship is at sea, but not enough people to control it */
- double dmg = config_get_flt("rules.ship.damage.nocrewocean", 0.3);
+ if (sh->number > 0) {
+ if (!sh->type->construction || sh->size >= sh->type->construction->maxsize) {
+ unit *cap = ship_owner(sh);
+ if (fval(r->terrain, SEA_REGION)) {
+ if (!ship_crewed(sh, cap)) {
+ /* ship is at sea, but not enough people to control it */
+ double dmg = config_get_flt("rules.ship.damage.nocrewocean", 0.3);
+ damage_ship(sh, dmg);
+ }
+ }
+ else if (!cap) {
+ /* any ship lying around without an owner slowly rots */
+ double dmg = config_get_flt("rules.ship.damage.nocrew", 0.05);
damage_ship(sh, dmg);
}
}
- else if (!ship_owner(sh)) {
- /* any ship lying around without an owner slowly rots */
- double dmg = config_get_flt("rules.ship.damage.nocrew", 0.05);
- damage_ship(sh, dmg);
+ if (sh->damage >= sh->size * DAMAGE_SCALE) {
+ sink_ship(sh);
+ remove_ship(shp, sh);
}
}
- if (sh->damage >= sh->size * DAMAGE_SCALE) {
- sink_ship(sh);
+ else {
remove_ship(shp, sh);
}
if (*shp == sh)
@@ -3990,6 +3991,7 @@ void init_processor(void)
p += 10;
add_proc_global(p, renumber_factions, "Neue Nummern");
}
+ add_proc_order(p, K_LOCALE, locale_cmd, 0, "Sprache wechseln");
}
static void reset_game(void)
@@ -4060,31 +4062,30 @@ void turn_end(void)
update_spells();
}
-/** determine if unit can be seen by faction
+/**
+ * Determine if unit can be seen by faction.
+ *
* @param f -- the observiong faction
* @param u -- the unit that is observed
* @param r -- the region that u is obesrved from (see below)
* @param m -- terrain modifier to stealth
- *
+ *
* r kann != u->region sein, wenn es um Durchreisen geht,
* oder Zauber (sp_generous, sp_fetchastral).
* Es muss auch niemand aus f in der region sein, wenn sie vom Turm
- * erblickt wird */
-bool
-cansee(const faction * f, const region * r, const unit * u, int modifier)
+ * erblickt wird.
+ */
+bool cansee(const faction * f, const region * r, const unit * u, int modifier)
{
int stealth, rings;
if (u->faction == f || omniscient(f)) {
return true;
}
- else if (fval(u_race(u), RCF_INVISIBLE)) {
- return false;
- }
else if (u->number == 0) {
attrib *a = a_find(u->attribs, &at_creator);
- if (a) { /* u is an empty temporary unit. In this special case
- we look at the creating unit. */
+ if (a) {
+ /* u is an empty temporary unit. In this special case we look at the creating unit. */
u = (unit *)a->data.v;
}
else {
@@ -4124,7 +4125,7 @@ cansee(const faction * f, const region * r, const unit * u, int modifier)
bool cansee_unit(const unit * u, const unit * target, int modifier)
/* target->region kann != u->region sein, wenn es um durchreisen geht */
{
- if (fval(u_race(target), RCF_INVISIBLE) || target->number == 0)
+ if (target->number == 0)
return false;
else if (target->faction == u->faction)
return true;
@@ -4167,7 +4168,7 @@ cansee_durchgezogen(const faction * f, const region * r, const unit * u,
{
unit *u2;
- if (fval(u_race(u), RCF_INVISIBLE) || u->number == 0)
+ if (u->number == 0)
return false;
else if (u->faction == f)
return true;
@@ -4210,3 +4211,20 @@ seefaction(const faction * f, const region * r, const unit * u, int modifier)
return true;
return false;
}
+
+int locale_cmd(unit * u, order * ord)
+{
+ char token[128];
+ const char * name;
+ faction *f = u->faction;
+
+ init_order(ord, f->locale);
+ name = gettoken(token, sizeof(token));
+ if (name) {
+ const struct locale *lang = get_locale(name);
+ if (lang && lang != f->locale) {
+ change_locale(f, lang);
+ }
+ }
+ return 0;
+}
diff --git a/src/laws.h b/src/laws.h
index a49848ac9..bb9ed05dc 100755
--- a/src/laws.h
+++ b/src/laws.h
@@ -50,6 +50,7 @@ extern "C" {
bool long_order_allowed(const struct unit *u);
bool password_wellformed(const char *password);
+ int locale_cmd(struct unit *u, struct order *ord);
int password_cmd(struct unit *u, struct order *ord);
int banner_cmd(struct unit *u, struct order *ord);
int email_cmd(struct unit *u, struct order *ord);
diff --git a/src/laws.test.c b/src/laws.test.c
index 45a5f3023..71ac067fc 100644
--- a/src/laws.test.c
+++ b/src/laws.test.c
@@ -61,7 +61,7 @@ static void test_rename_building(CuTest * tc)
test_create_locale();
btype = test_create_buildingtype("castle");
r = test_create_region(0, 0, NULL);
- b = new_building(btype, r, default_locale);
+ b = new_building(btype, r, default_locale, 1);
f = test_create_faction(NULL);
u = test_create_unit(f, r);
u_set_building(u, b);
@@ -84,7 +84,7 @@ static void test_rename_building_twice(CuTest * tc)
test_create_locale();
btype = test_create_buildingtype("castle");
r = test_create_region(0, 0, NULL);
- b = new_building(btype, r, default_locale);
+ b = new_building(btype, r, default_locale, 1);
f = test_create_faction(NULL);
u = test_create_unit(f, r);
u_set_building(u, b);
diff --git a/src/magic.c b/src/magic.c
index 5300101d3..578557b23 100644
--- a/src/magic.c
+++ b/src/magic.c
@@ -22,12 +22,15 @@
#include
#include
+#include
#include
#include
#include
#include
#include
+#include
#include
+#include
#include
#include
#include
@@ -44,10 +47,8 @@
#include
/* util includes */
-#include
+#include
#include
-#include
-#include
#include
#include
#include
@@ -2927,10 +2928,14 @@ const char *spell_info(const spell * sp, const struct locale *lang)
return LOC(lang, mkname("spellinfo", sp->sname));
}
-/* TODO: should take the name, not the spell (spellref optimizations) */
-const char *spell_name(const spell * sp, const struct locale *lang)
+const char *mkname_spell(const spell *sp)
{
- return LOC(lang, mkname("spell", sp->sname));
+ return mkname("spell", sp->sname);
+}
+
+const char *spell_name(const char *spname, const struct locale *lang)
+{
+ return LOC(lang, spname);
}
const char *curse_name(const curse_type * ctype, const struct locale *lang)
@@ -2948,14 +2953,22 @@ static void select_spellbook(void **tokens, spellbook *sb, const struct locale *
for (qi = 0, ql = sb->spells; ql; selist_advance(&ql, &qi, 1)) {
spellbook_entry *sbe = (spellbook_entry *)selist_get(ql, qi);
const spell *sp = spellref_get(&sbe->spref);
- const char *n = spell_name(sp, lang);
+ const char *spname = mkname("spell", sp->sname);
+ const char *n = spell_name(spname, lang);
if (!n) {
log_error("no translation in locale %s for spell %s\n", locale_name(lang), sp->sname);
}
else {
variant token;
+ const str_alias *aliases = alias_get(lang, spname);
token.v = (void *)sp;
addtoken((struct tnode **)tokens, n, token);
+ if (aliases) {
+ int i;
+ for (i = 0; i != MAXSTRINGS && aliases->strings[i]; ++i) {
+ addtoken((struct tnode **)tokens, aliases->strings[i], token);
+ }
+ }
}
}
}
diff --git a/src/magic.h b/src/magic.h
index c373d2ecf..068ab223e 100644
--- a/src/magic.h
+++ b/src/magic.h
@@ -315,10 +315,10 @@ extern "C" {
void fix_fam_spells(struct unit *u);
void fix_fam_migrant(struct unit *u);
+ const char *mkname_spell(const struct spell *sp);
+ const char *spell_name(const char *spname, const struct locale *lang);
const char *spell_info(const struct spell *sp,
const struct locale *lang);
- const char *spell_name(const struct spell *sp,
- const struct locale *lang);
const char *curse_name(const struct curse_type *ctype,
const struct locale *lang);
diff --git a/src/magic.test.c b/src/magic.test.c
index 068efdfa4..9c4b428a8 100644
--- a/src/magic.test.c
+++ b/src/magic.test.c
@@ -189,7 +189,7 @@ void test_getspell_unit(CuTest * tc)
lang = test_create_locale();
sp = create_spell("testspell");
- locale_setstring(lang, mkname("spell", sp->sname), "Herp-a-derp");
+ locale_setstring(lang, mkname_spell(sp), "Herp-a-derp");
CuAssertPtrEquals(tc, NULL, unit_getspell(u, "Herp-a-derp", lang));
@@ -248,7 +248,7 @@ void test_getspell_school(CuTest * tc)
lang = test_create_locale();
sp = create_spell("testspell");
- locale_setstring(lang, mkname("spell", sp->sname), "Herp-a-derp");
+ locale_setstring(lang, mkname_spell(sp), "Herp-a-derp");
CuAssertPtrEquals(tc, NULL, unit_getspell(u, "Herp-a-derp", lang));
@@ -402,8 +402,8 @@ void test_multi_cast(CuTest *tc) {
CuAssertPtrEquals(tc, sp, find_spell("fireball"));
lang = test_create_locale();
- locale_setstring(lang, mkname("spell", sp->sname), "Feuerball");
- CuAssertStrEquals(tc, "Feuerball", spell_name(sp, lang));
+ locale_setstring(lang, mkname_spell(sp), "Feuerball");
+ CuAssertStrEquals(tc, "Feuerball", spell_name(mkname_spell(sp), lang));
u = test_create_unit(test_create_faction(NULL), test_create_plain(0, 0));
set_level(u, SK_MAGIC, 10);
diff --git a/src/monsters.c b/src/monsters.c
index 34214a342..bd156238b 100644
--- a/src/monsters.c
+++ b/src/monsters.c
@@ -205,8 +205,13 @@ void monsters_desert(struct faction *monsters)
unit *u;
for (u = r->units; u; u = u->next) {
- if (u->faction != monsters
- && (u_race(u)->flags & RCF_DESERT)) {
+ if (u->faction == monsters) {
+ const struct race * rc = u_race(u);
+ if (rc->splitsize < 10) {
+ /* hermit-type monsters eat each other */
+ monster_cannibalism(u);
+ }
+ } else if (u_race(u)->flags & RCF_DESERT) {
if (fval(u, UFL_ISNEW))
continue;
if (rng_int() % 100 < 5) {
@@ -497,7 +502,7 @@ static attrib *set_new_dragon_target(unit * u, region * r, int range)
{
int max_affinity = 0;
region *max_region = NULL;
- selist *ql, *rlist = regions_in_range(r, range, allowed_dragon);
+ selist *ql, *rlist = path_regions_in_range(r, range, allowed_dragon);
int qi;
for (qi = 0, ql = rlist; ql; selist_advance(&ql, &qi, 1)) {
@@ -752,7 +757,8 @@ static order *plan_dragon(unit * u)
return long_order;
}
-static void monster_cannibalism(unit *u) {
+void monster_cannibalism(unit *u)
+{
unit *u2;
for (u2 = u->next; u2; u2 = u2->next) {
@@ -792,11 +798,6 @@ void plan_monsters(faction * f)
}
a_removeall(&u->attribs, &at_otherfaction);
- 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/monsters.h b/src/monsters.h
index d87398c2c..c44ef1a00 100644
--- a/src/monsters.h
+++ b/src/monsters.h
@@ -16,6 +16,7 @@ extern "C" {
struct unit *spawn_seaserpent(struct region *r, struct faction *f);
void spawn_dragons(void);
void monsters_desert(struct faction *monsters);
+ void monster_cannibalism(struct unit *u);
void monster_kills_peasants(struct unit *u);
void make_zombie(struct unit * u);
diff --git a/src/move.c b/src/move.c
index 5d5513cd3..3976b79bf 100644
--- a/src/move.c
+++ b/src/move.c
@@ -819,7 +819,7 @@ static void drifting_ships(region * r)
/* Kapitaen da? Beschaedigt? Genuegend Matrosen?
* Genuegend leicht? Dann ist alles OK. */
- if (ship_finished(sh) && ship_crewed(sh) && cansail(r, sh)) {
+ if (ship_finished(sh) && ship_crewed(sh, ship_owner(sh)) && cansail(r, sh)) {
shp = &sh->next;
continue;
}
@@ -1630,7 +1630,8 @@ static const region_list *travel_route(unit * u,
static bool ship_ready(const region * r, unit * u, order * ord)
{
- if (!u->ship || u != ship_owner(u->ship)) {
+ unit *cap = u->ship ? ship_owner(u->ship) : NULL;
+ if (u != cap) {
cmistake(u, ord, 146, MSG_MOVE);
return false;
}
@@ -1640,11 +1641,15 @@ static bool ship_ready(const region * r, unit * u, order * ord)
u->ship));
return false;
}
+ if (u->number < u->ship->number) {
+ cmistake(u, ord, 329, MSG_MOVE);
+ return false;
+ }
if (!ship_finished(u->ship)) {
cmistake(u, ord, 15, MSG_MOVE);
return false;
}
- if (!ship_crewed(u->ship)) {
+ if (!ship_crewed(u->ship, u)) {
cmistake(u, ord, 1, MSG_MOVE);
return false;
}
diff --git a/src/orderfile.c b/src/orderfile.c
index a42d5ee69..0f0c951a2 100644
--- a/src/orderfile.c
+++ b/src/orderfile.c
@@ -11,7 +11,6 @@
#include "util/message.h"
#include "util/language.h"
#include "util/log.h"
-#include "util/filereader.h"
#include "util/param.h"
#include "util/parser.h"
#include "util/password.h"
diff --git a/src/races/races.c b/src/races/races.c
index 7c3e65fc4..a782ba0ce 100644
--- a/src/races/races.c
+++ b/src/races/races.c
@@ -42,8 +42,7 @@ void equip_newunits(struct unit *u)
if (u->building == NULL) {
const building_type *btype = bt_find("castle");
if (btype != NULL) {
- building *b = new_building(btype, r, u->faction->locale);
- b->size = 10;
+ building *b = new_building(btype, r, u->faction->locale, 10);
u_set_building(u, b);
building_set_owner(u);
}
diff --git a/src/report.c b/src/report.c
index 1560f42e2..f283b552f 100644
--- a/src/report.c
+++ b/src/report.c
@@ -256,7 +256,7 @@ void nr_spell_syntax(char *buf, size_t size, spellbook_entry * sbe, const struct
sbs_strcat(&sbs, " n]");
}
- spname = spell_name(sp, lang);
+ spname = spell_name(mkname_spell(sp), lang);
if (strchr(spname, ' ') != NULL) {
/* contains spaces, needs quotes */
sbs_strcat(&sbs, " '");
@@ -422,7 +422,7 @@ void nr_spell(struct stream *out, spellbook_entry * sbe, const struct locale *la
sbstring sbs;
newline(out);
- centre(out, spell_name(sp, lang), true);
+ centre(out, spell_name(mkname_spell(sp), lang), true);
newline(out);
paragraph(out, LOC(lang, "nr_spell_description"), 0, 0, 0);
paragraph(out, spell_info(sp, lang), 2, 0, 0);
@@ -627,9 +627,6 @@ nr_unit(struct stream *out, const faction * f, const unit * u, int indent, seen_
bool isbattle = (mode == seen_battle);
char buf[8192];
- if (fval(u_race(u), RCF_INVISIBLE))
- return;
-
newline(out);
dh = bufunit_depr(f, u, mode, buf, sizeof(buf));
@@ -1155,7 +1152,7 @@ void report_region(struct stream *out, const region * r, faction * f)
if (r->seen.mode >= seen_unit) {
report_region_schemes(out, r, f);
}
- if (r->seen.mode >= seen_lighthouse) {
+ if (r->seen.mode >= seen_lighthouse_land) {
report_region_edges(out, r, f, edges, ne);
}
}
@@ -1231,7 +1228,7 @@ static void report_statistics(struct stream *out, const region * r, const factio
/* count */
for (number = 0, u = r->units; u; u = u->next) {
- if (u->faction == f && !fval(u_race(u), RCF_INVISIBLE)) {
+ if (u->faction == f) {
for (itm = u->items; itm; itm = itm->next) {
i_change(&items, itm->type, itm->number);
}
@@ -1321,7 +1318,7 @@ report_template(const char *filename, report_context * ctx, const char *bom)
continue;
for (u = r->units; u; u = u->next) {
- if (u->faction == f && !fval(u_race(u), RCF_INVISIBLE)) {
+ if (u->faction == f) {
order *ord;
if (!dh) {
plane *pl = getplane(r);
diff --git a/src/reports.c b/src/reports.c
index 2553920d1..55ecdafdd 100644
--- a/src/reports.c
+++ b/src/reports.c
@@ -808,7 +808,7 @@ void bufunit(const faction * f, const unit * u, const faction *fv,
sbs_strcat(sbp, ", ");
}
/* TODO: no need to deref the spellref here (spref->name is good) */
- sbs_strcat(sbp, spell_name(sp, lang));
+ sbs_strcat(sbp, spell_name(mkname_spell(sp), lang));
}
}
@@ -830,7 +830,7 @@ void bufunit(const faction * f, const unit * u, const faction *fv,
sp = get_combatspell(u, i);
if (sp) {
int sl = get_combatspelllevel(u, i);
- sbs_strcat(sbp, spell_name(sp, lang));
+ sbs_strcat(sbp, spell_name(mkname_spell(sp), lang));
if (sl > 0) {
sbs_printf(sbp, "(%d)", sl);
}
@@ -1877,7 +1877,7 @@ static void eval_spell(struct opstack **stack, const void *userdata)
const struct faction *f = (const struct faction *)userdata;
const struct spell *sp = (const struct spell *)opop(stack).v;
const char *c =
- sp ? spell_name(sp, f->locale) : LOC(f->locale, "an_unknown_spell");
+ sp ? spell_name(mkname_spell(sp), f->locale) : LOC(f->locale, "an_unknown_spell");
variant var;
assert(c || !"spell without description!");
@@ -2369,8 +2369,10 @@ bool visible_unit(const unit *u, const faction *f, int stealthmod, seen_mode mod
return true;
}
else {
- if (stealthmod > INT_MIN && mode >= seen_lighthouse) {
- if (mode != seen_travel || u->building || u->ship || is_guard(u)) {
+ if (stealthmod > INT_MIN) {
+ if (mode == seen_lighthouse) {
+ return u_race(u)->weight >= 5000;
+ } else if (mode > seen_travel || u->building || u->ship || is_guard(u)) {
return cansee(f, u->region, u, stealthmod);
}
}
diff --git a/src/reports.test.c b/src/reports.test.c
index 0a85b75d2..bcf644ebc 100644
--- a/src/reports.test.c
+++ b/src/reports.test.c
@@ -870,8 +870,9 @@ static void test_visible_unit(CuTest *tc) {
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));
+ rc->weight = 5000;
+ CuAssertTrue(tc, visible_unit(u, f, -2, seen_lighthouse));
u->ship = sh = test_create_ship(u->region, NULL);
CuAssertTrue(tc, visible_unit(u, f, -2, seen_travel));
diff --git a/src/spells.c b/src/spells.c
index de2036225..604810d1c 100644
--- a/src/spells.c
+++ b/src/spells.c
@@ -3749,7 +3749,7 @@ static int sp_migranten(castorder * co)
unit *target;
region *r = co_get_region(co);
unit *mage = co_get_caster(co);
- int cast_level = co->level;
+ int max_force = (int) co->force;
spellparameter *pa = co->par;
/* wenn kein Ziel gefunden, Zauber abbrechen */
@@ -3782,7 +3782,7 @@ static int sp_migranten(castorder * co)
return 0;
}
/* maximal Stufe Personen */
- if (target->number > cast_level || target->number > max_spellpoints_depr(r, mage)) {
+ if (target->number > max_force || target->number > max_spellpoints_depr(r, mage)) {
ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order,
"spellfail_toomanytargets", ""));
return 0;
@@ -4415,7 +4415,7 @@ int sp_icastle(castorder * co)
const building_type *type;
region *r = co_get_region(co);
unit *mage = co_get_caster(co);
- int cast_level = co->level;
+ int size, cast_level = co->level;
double power = co->force;
spellparameter *pa = co->par;
const char *bname;
@@ -4431,18 +4431,17 @@ int sp_icastle(castorder * co)
type = bt_find("castle");
}
- b = new_building(bt_illusion, r, mage->faction->locale);
-
/* Groesse festlegen. */
if (type == bt_illusion) {
- b->size = (rng_int() % (int)((power * power) + 1) * 10);
+ size = (rng_int() % (int)((power * power) + 1) * 10);
}
else if (type->maxsize > 0) {
- b->size = type->maxsize;
+ size = type->maxsize;
}
else {
- b->size = ((rng_int() % (int)(power)) + 1) * 5;
+ size = ((rng_int() % (int)(power)) + 1) * 5;
}
+ b = new_building(bt_illusion, r, mage->faction->locale, size);
bname = LOC(mage->faction->locale, buildingtype(type, b, 0));
building_setname(b, bname);
@@ -4512,13 +4511,16 @@ int sp_illusionary_shapeshift(castorder * co)
return 0;
}
irace = u_irace(u);
- if (irace == u_race(u)) {
- trigger *trestore = trigger_changerace(u, NULL, irace);
- add_trigger(&u->attribs, "timer", trigger_timeout((int)power + 3,
- trestore));
- u->irace = rc;
+ if (irace != u_race(u)) {
+ ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order,
+ "sp_shapeshift_twice", "target", u));
+ return 0;
}
+ add_trigger(&u->attribs, "timer", trigger_timeout((int)power + 3,
+ trigger_changerace(u, NULL, irace)));
+ u->irace = rc;
+
ADDMSG(&mage->faction->msgs, msg_message("shapeshift_effect",
"mage target race", mage, u, rc));
@@ -5201,22 +5203,21 @@ int sp_leaveastral(castorder * co)
case 1:
rt = pa->param[0]->data.r;
if (!rt || r_standard_to_astral(rt) != r || !inhabitable(rt)) {
- ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order,
- "spellfail::noway", ""));
+ cmistake(mage, co->order, 216, MSG_MAGIC);
return 0;
}
ro = r;
break;
default:
ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order,
- "spell_astral_only", ""));
+ "spell_astral_only", NULL));
return 0;
}
if (ro == NULL || is_cursed(ro->attribs, &ct_astralblock)
|| is_cursed(rt->attribs, &ct_astralblock)) {
ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order,
- "spellfail_astralblock", ""));
+ "spellfail_astralblock", NULL));
return 0;
}
@@ -5421,130 +5422,93 @@ int sp_fetchastral(castorder * co)
return cast_level;
}
-#define SHOWASTRAL_IS_BORKED
-#ifndef SHOWASTRAL_IS_BORKED
+static bool cb_show_astral(const struct region *r) {
+ return r->units && !is_cursed(r->attribs, &ct_astralblock);
+}
+
int sp_showastral(castorder * co)
{
- unit *u;
region *rt;
int n = 0;
- int c = 0;
- region_list *rl, *rl2;
region *r = co_get_region(co);
unit *mage = co_get_caster(co);
- int cast_level = co->level;
- double power = co->force;
+ int force = (int) co->force;
+ int radius = (force < SHOWASTRAL_MAX_RADIUS) ? force : SHOWASTRAL_MAX_RADIUS;
+ region *targets[4 * SHOWASTRAL_MAX_RADIUS * SHOWASTRAL_MAX_RADIUS];
- switch (getplaneid(r)) {
- case 0:
- rt = r_standard_to_astral(r);
- if (!rt || fval(rt->terrain, FORBIDDEN_REGION)) {
- /* Hier gibt es keine Verbindung zur astralen Welt */
- cmistake(mage, co->order, 216, MSG_MAGIC);
- return 0;
- }
- break;
- case 1:
- rt = r;
- break;
- default:
+ if (getplaneid(r) == 1) {
+ ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order,
+ "spell_astral_forbidden", NULL));
+ return 0;
+ }
+ rt = r_standard_to_astral(r);
+ if (rt == NULL || fval(rt->terrain, FORBIDDEN_REGION) || is_cursed(r->attribs, &ct_astralblock)) {
/* Hier gibt es keine Verbindung zur astralen Welt */
cmistake(mage, co->order, 216, MSG_MAGIC);
return 0;
}
- rl = all_in_range(rt, power / 5);
-
- /* Erst Einheiten zaehlen, fuer die Grammatik. */
-
- for (rl2 = rl; rl2; rl2 = rl2->next) {
- region *r2 = rl2->data;
- if (!is_cursed(r2->attribs, &ct_astralblock)) {
- for (u = r2->units; u; u = u->next) {
- n++;
- }
- }
- }
-
+ n = regions_in_range(rt, radius, cb_show_astral, targets);
if (n == 0) {
/* sprintf(buf, "%s kann niemanden im astralen Nebel entdecken.",
unitname(mage)); */
cmistake(mage, co->order, 220, MSG_MAGIC);
+ return 0;
}
else {
-
- /* Ausgeben */
-
- sprintf(buf, "%s hat eine Vision der astralen Ebene. Im astralen "
- "Nebel zu erkennen sind ", unitname(mage));
-
- for (rl2 = rl; rl2; rl2 = rl2->next) {
- if (!is_cursed(rl2->data->attribs, &ct_astralblock)) {
- for (u = rl2->data->units; u; u = u->next) {
- c++;
- scat(unitname(u));
- scat(" (");
- if (!fval(u, UFL_ANON_FACTION)) {
- scat(factionname(u->faction));
- scat(", ");
- }
- icat(u->number);
- scat(" ");
- scat(LOC(mage->faction->locale, rc_name_s(u_race(u), (u->number == 1) ? NAME_SINGULAR : NAME_PLURAL)));
- scat(", Entfernung ");
- icat(distance(rl2->data, rt));
- scat(")");
- if (c == n - 1) {
- scat(" und ");
- }
- else if (c < n - 1) {
- scat(", ");
- }
- }
- }
+ int i;
+ for (i = 0; i != n; ++i) {
+ region *rt = targets[i];
+ set_observer(rt, mage->faction, (int)(co->force / 2), 2);
}
- scat(".");
- addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO);
+ ADDMSG(&mage->faction->msgs, msg_message("showastral_effect", "unit", mage));
}
- free_regionlist(rl);
- return cast_level;
+ return co->level;
}
-#endif
/* ------------------------------------------------------------- */
+
+static bool cb_view_reality(const struct region *r) {
+ return !is_cursed(r->attribs, &ct_astralblock);
+}
+
int sp_viewreality(castorder * co)
{
region *r = co_get_region(co);
unit *mage = co_get_caster(co);
- int cast_level = co->level;
- message *m;
+ int force = (int)co->force;
region *rl[MAX_SCHEMES];
int num;
if (getplaneid(r) != 1) {
/* sprintf(buf, "Dieser Zauber kann nur im Astralraum gezaubert werden."); */
ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order,
- "spell_astral_only", ""));
+ "spell_astral_only", NULL));
return 0;
}
- num = get_astralregions(r, NULL, rl);
+ if (is_cursed(r->attribs, &ct_astralblock)) {
+ cmistake(mage, co->order, 216, MSG_MAGIC);
+ return 0;
+ }
+
+ num = get_astralregions(r, cb_view_reality, rl);
if (num > 0) {
int i;
for (i = 0; i != num; ++i) {
region *rt = rl[i];
- if (!is_cursed(rt->attribs, &ct_astralblock)) {
- set_observer(rt, mage->faction, co->level / 2, 2);
- }
+ set_observer(rt, mage->faction, force / 2, 2);
}
}
+ else {
+ cmistake(mage, co->order, 216, MSG_MAGIC);
+ return 0;
+ }
- m = msg_message("viewreality_effect", "unit", mage);
- r_addmessage(r, mage->faction, m);
- msg_release(m);
+ ADDMSG(&mage->faction->msgs, msg_message("viewreality_effect", "unit", mage));
- return cast_level;
+ return co->level;
}
static void cb_disrupt_astral(region *r2, void *cbdata) {
@@ -6457,9 +6421,7 @@ static spelldata spell_functions[] = {
{ "analyze_magic", sp_analysemagic, 0 },
{ "concealing_aura", sp_itemcloak, 0 },
{ "tybiedfumbleshield", sp_fumbleshield, 0 },
-#ifndef SHOWASTRAL_IS_BORKED
{ "show_astral", sp_showastral, 0 },
-#endif
{ "resist_magic", sp_resist_magic_bonus, 0 },
{ "keeploot", sp_keeploot, 0 },
{ "enterastral", sp_enterastral, 0 },
diff --git a/src/spells.h b/src/spells.h
index 5a82d5b6f..86e15bd93 100644
--- a/src/spells.h
+++ b/src/spells.h
@@ -16,10 +16,11 @@ extern "C" {
void register_magicresistance(void);
void register_spells(void);
+#define SHOWASTRAL_MAX_RADIUS 5
int sp_baddreams(struct castorder * co);
int sp_gooddreams(struct castorder * co);
int sp_viewreality(struct castorder * co);
-
+ int sp_showastral(struct castorder * co);
#define ACTION_RESET 0x01 /* reset the one-time-flag FFL_SELECT (on first pass) */
#define ACTION_CANSEE 0x02 /* to people who can see the actor */
#define ACTION_CANNOTSEE 0x04 /* to people who can not see the actor */
diff --git a/src/spells.test.c b/src/spells.test.c
index 6ee4a305c..06572229c 100644
--- a/src/spells.test.c
+++ b/src/spells.test.c
@@ -116,39 +116,150 @@ static void test_bad_dreams(CuTest *tc) {
}
static void test_view_reality(CuTest *tc) {
- region *r, *ra;
+ region *r, *ra, *rx;
faction *f;
unit *u;
castorder co;
+ curse *c;
test_setup();
+ mt_create_error(216);
+ mt_create_error(220);
mt_create_va(mt_new("spell_astral_only", NULL),
"unit:unit", "region:region", "command:order", MT_NEW_END);
mt_create_va(mt_new("viewreality_effect", NULL),
"unit:unit", MT_NEW_END);
- r = test_create_region(0, 0, NULL);
- ra = test_create_region(real2tp(r->x), real2tp(r->y), NULL);
- ra->_plane = get_astralplane();
+ rx = test_create_region(0, TP_RADIUS + 1, NULL);
f = test_create_faction(NULL);
- u = test_create_unit(f, r);
+ u = test_create_unit(f, rx);
- test_create_castorder(&co, u, 10, 10., 0, NULL);
- CuAssertIntEquals(tc, -1, get_observer(r, f));
+ /* can only cast in astral space */
+ test_create_castorder(&co, u, 10, 10.0, 0, NULL);
CuAssertIntEquals(tc, 0, sp_viewreality(&co));
CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "spell_astral_only"));
free_castorder(&co);
test_clear_messagelist(&f->msgs);
+ ra = test_create_region(real2tp(0), real2tp(0), NULL);
+ ra->_plane = get_astralplane();
move_unit(u, ra, NULL);
- test_create_castorder(&co, u, 9, 10., 0, NULL);
- CuAssertIntEquals(tc, -1, get_observer(r, f));
+ /* there is no connection from ra to rx */
+ test_create_castorder(&co, u, 10, 10.0, 0, NULL);
+ CuAssertIntEquals(tc, 0, sp_viewreality(&co));
+ CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error216"));
+ CuAssertIntEquals(tc, -1, get_observer(rx, f));
+ free_castorder(&co);
+
+ test_clear_messagelist(&f->msgs);
+ r = test_create_region(0, 0, NULL);
+
+ test_clear_messagelist(&f->msgs);
+
+ /* units exist, r can be seen, but rx is out of range */
+ test_create_castorder(&co, u, 9, 10.0, 0, NULL);
CuAssertIntEquals(tc, 9, sp_viewreality(&co));
- CuAssertPtrEquals(tc, NULL, test_find_messagetype(f->msgs, "spell_astral_only"));
- CuAssertIntEquals(tc, 4, get_observer(r, f));
- CuAssertPtrEquals(tc, f, (void *)ra->individual_messages->viewer);
- CuAssertPtrNotNull(tc, test_find_messagetype(ra->individual_messages->msgs, "viewreality_effect"));
+ CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "viewreality_effect"));
+ CuAssertIntEquals(tc, 5, get_observer(r, f));
+ CuAssertIntEquals(tc, -1, get_observer(rx, f));
+ free_castorder(&co);
+
+ set_observer(r, f, -1, -1);
+ CuAssertIntEquals(tc, -1, get_observer(r, f));
+
+ /* target region r exists, but astral space is blocked */
+ c = create_curse(NULL, &ra->attribs, &ct_astralblock, 50.0, 1, 50, 0);
+ test_create_castorder(&co, u, 10, 10.0, 0, NULL);
+ CuAssertIntEquals(tc, 0, sp_viewreality(&co));
+ CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error216"));
+ CuAssertIntEquals(tc, -1, get_observer(r, f));
+ free_castorder(&co);
+ remove_curse(&ra->attribs, c);
+
+ /* target region r exists, but astral interference is blocked */
+ c = create_curse(NULL, &r->attribs, &ct_astralblock, 50.0, 1, 50, 0);
+ test_create_castorder(&co, u, 10, 10.0, 0, NULL);
+ CuAssertIntEquals(tc, 0, sp_viewreality(&co));
+ CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error216"));
+ CuAssertIntEquals(tc, -1, get_observer(r, f));
+ free_castorder(&co);
+
+ test_teardown();
+}
+
+static void test_show_astral(CuTest *tc) {
+ region *r, *ra, *rx;
+ faction *f;
+ unit *u;
+ castorder co;
+ curse * c;
+
+ test_setup();
+ mt_create_error(216);
+ mt_create_error(220);
+ mt_create_va(mt_new("spell_astral_forbidden", NULL),
+ "unit:unit", "region:region", "command:order", MT_NEW_END);
+ mt_create_va(mt_new("showastral_effect", NULL),
+ "unit:unit", MT_NEW_END);
+ ra = test_create_region(real2tp(0), real2tp(0) + 1 + SHOWASTRAL_MAX_RADIUS, NULL);
+ ra->_plane = get_astralplane();
+ f = test_create_faction(NULL);
+ u = test_create_unit(f, ra);
+
+ /* error: unit is in astral space */
+ test_create_castorder(&co, u, 10, 10.0, 0, NULL);
+ CuAssertIntEquals(tc, 0, sp_showastral(&co));
+ CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "spell_astral_forbidden"));
+ free_castorder(&co);
+
+ test_clear_messagelist(&f->msgs);
+ r = test_create_region(0, 0, NULL);
+ move_unit(u, r, NULL);
+
+ /* error: no target region */
+ test_create_castorder(&co, u, 9, 10.0, 0, NULL);
+ CuAssertIntEquals(tc, 0, sp_showastral(&co));
+ CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error216"));
+ CuAssertIntEquals(tc, -1, get_observer(ra, f));
+ free_castorder(&co);
+
+ rx = test_create_region(real2tp(r->x), real2tp(r->y), NULL);
+ rx->_plane = ra->_plane;
+
+ /* rx is in range, but empty */
+ test_create_castorder(&co, u, 9, 10.0, 0, NULL);
+ CuAssertIntEquals(tc, 0, sp_showastral(&co));
+ CuAssertIntEquals(tc, -1, get_observer(rx, f));
+ CuAssertIntEquals(tc, -1, get_observer(ra, f));
+ CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error220"));
+ CuAssertPtrEquals(tc, NULL, test_find_messagetype(f->msgs, "showastral_effect"));
+ free_castorder(&co);
+
+ test_create_unit(f, ra);
+ test_create_unit(f, rx);
+ /* rx is in range, but ra is not */
+ test_create_castorder(&co, u, 9, 10.0, 0, NULL);
+ CuAssertIntEquals(tc, 9, sp_showastral(&co));
+ CuAssertIntEquals(tc, 5, get_observer(rx, f));
+ CuAssertIntEquals(tc, -1, get_observer(ra, f));
+ CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "showastral_effect"));
+ free_castorder(&co);
+
+ /* astral block on r */
+ c = create_curse(NULL, &r->attribs, &ct_astralblock, 50.0, 1, 50, 0);
+ test_create_castorder(&co, u, 9, 10.0, 0, NULL);
+ CuAssertIntEquals(tc, 0, sp_showastral(&co));
+ CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error216"));
+ free_castorder(&co);
+ remove_curse(&r->attribs, c);
+
+ /* astral block on rx */
+ c = create_curse(NULL, &rx->attribs, &ct_astralblock, 50.0, 1, 50, 0);
+ test_create_castorder(&co, u, 9, 10.0, 0, NULL);
+ CuAssertIntEquals(tc, 0, sp_showastral(&co));
+ CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error220"));
free_castorder(&co);
+ remove_curse(&rx->attribs, c);
test_teardown();
}
@@ -174,6 +285,7 @@ CuSuite *get_spells_suite(void)
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_watch_region);
SUITE_ADD_TEST(suite, test_view_reality);
+ SUITE_ADD_TEST(suite, test_show_astral);
SUITE_ADD_TEST(suite, test_good_dreams);
SUITE_ADD_TEST(suite, test_bad_dreams);
SUITE_ADD_TEST(suite, test_dreams);
diff --git a/src/spells/unitcurse.c b/src/spells/unitcurse.c
index 095649562..345f75109 100644
--- a/src/spells/unitcurse.c
+++ b/src/spells/unitcurse.c
@@ -318,10 +318,10 @@ static message *cinfo_skillmod(const void *obj, objtype_t typ, const curse * c,
unit *u = (unit *)obj;
int sk = c->data.i;
if (c->effect > 0) {
- return msg_message("curseinfo::skill_1", "unit skill id", u, sk, c->no);
+ return msg_message("curseinfo_skill_1", "unit skill id", u, sk, c->no);
}
else if (c->effect < 0) {
- return msg_message("curseinfo::skill_2", "unit skill id", u, sk, c->no);
+ return msg_message("curseinfo_skill_2", "unit skill id", u, sk, c->no);
}
}
return NULL;
diff --git a/src/teleport.c b/src/teleport.c
index ecd3834bf..067764946 100644
--- a/src/teleport.c
+++ b/src/teleport.c
@@ -22,7 +22,6 @@
#include
#define TE_CENTER 1000
-#define TP_DISTANCE 4
int real2tp(int rk)
{
@@ -43,32 +42,37 @@ static region *tpregion(const region * r)
return rt;
}
-
-int get_astralregions(const region * r, bool(*valid) (const region *), region *result[])
+int regions_in_range(const region * r, int radius, bool(*valid) (const region *), region *result[])
{
- assert(is_astral(r));
- r = r_astral_to_standard(r);
- if (r) {
- int x, y, num = 0;
- for (x = -TP_RADIUS; x <= +TP_RADIUS; ++x) {
- for (y = -TP_RADIUS; y <= +TP_RADIUS; ++y) {
+ int x, y, num = 0;
+ const struct plane *pl = rplane(r);
+ for (x = -radius; x <= +radius; ++x) {
+ for (y = -radius; y <= radius; ++y) {
+ int dist = koor_distance(0, 0, x, y);
+
+ if (dist <= radius) {
region *rn;
- int dist = koor_distance(0, 0, x, y);
-
- if (dist <= TP_RADIUS) {
- int nx = r->x + x, ny = r->y + y;
- pnormalize(&nx, &ny, rplane(r));
- rn = findregion(nx, ny);
- if (rn != NULL && (valid == NULL || valid(rn))) {
- if (result) {
- result[num] = rn;
- }
- ++num;
+ int nx = r->x + x, ny = r->y + y;
+ pnormalize(&nx, &ny, pl);
+ rn = findregion(nx, ny);
+ if (rn != NULL && (valid == NULL || valid(rn))) {
+ if (result) {
+ result[num] = rn;
}
+ ++num;
}
}
}
- return num;
+ }
+ return num;
+}
+
+int get_astralregions(const region * r, bool(*valid) (const region *), region *result[])
+{
+ assert(is_astral(r));
+ r = r_astral_to_standard(r);
+ if (r) {
+ return regions_in_range(r, TP_RADIUS, valid, result);
}
return 0;
}
diff --git a/src/teleport.h b/src/teleport.h
index d91bed3df..9930a010c 100644
--- a/src/teleport.h
+++ b/src/teleport.h
@@ -7,7 +7,8 @@
extern "C" {
#endif
-#define TP_RADIUS 2 /* Radius von Schemen */
+#define TP_DISTANCE 4
+#define TP_RADIUS (TP_DISTANCE/2) /* Radius von Schemen */
#define MAX_SCHEMES ((TP_RADIUS * 2 + 1) * (TP_RADIUS * 2 + 1) - 4)
struct region;
@@ -20,6 +21,7 @@ extern "C" {
bool is_astral(const struct region *r);
struct plane *get_astralplane(void);
int get_astralregions(const struct region * r, bool(*valid) (const struct region *), struct region *result[]);
+ int regions_in_range(const struct region * r, int radius, bool(*valid) (const struct region *), struct region *result[]);
void create_teleport_plane(void);
void spawn_braineaters(float chance);
diff --git a/src/tests.c b/src/tests.c
index 727803e2d..9a6ac156d 100644
--- a/src/tests.c
+++ b/src/tests.c
@@ -27,13 +27,14 @@
#include
#include
+#include
#include
-#include "util/keyword.h"
+#include
#include
#include
#include
#include
-#include "util/param.h"
+#include
#include
#include
@@ -249,6 +250,7 @@ static void test_reset(void) {
free_shiptypes();
free_races();
free_spellbooks();
+ free_aliases();
free_prefixes();
mt_clear();
@@ -335,8 +337,7 @@ building * test_create_building(region * r, const building_type * btype)
bt_castle->flags |= BTF_FORTIFICATION;
btype = bt_castle;
}
- b = new_building(btype, r, default_locale);
- b->size = btype->maxsize > 0 ? btype->maxsize : 1;
+ b = new_building(btype, r, default_locale, (btype->maxsize > 0) ? btype->maxsize : 1);
return b;
}
diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt
index 19bf70b12..5a0b57e6b 100644
--- a/src/util/CMakeLists.txt
+++ b/src/util/CMakeLists.txt
@@ -3,10 +3,10 @@ project(util C)
add_subdirectory (crypto)
SET(_TEST_FILES
+# aliases.test.c
base36.test.c
# crmessage.test.c
# dice.test.c
-# filereader.test.c
functions.test.c
# goodies.test.c
keyword.test.c
@@ -32,10 +32,10 @@ variant.test.c
)
SET(_FILES
+aliases.c
base36.c
crmessage.c
dice.c
-filereader.c
functions.c
goodies.c
keyword.c
diff --git a/src/util/aliases.c b/src/util/aliases.c
new file mode 100644
index 000000000..bec4dabcd
--- /dev/null
+++ b/src/util/aliases.c
@@ -0,0 +1,79 @@
+#include "aliases.h"
+
+#include
+
+#include
+#include
+#include
+
+static struct str_aliases *g_aliases;
+
+void free_aliases(void)
+{
+ while (g_aliases) {
+ int i;
+ struct str_aliases * anext = g_aliases->next;
+ for (i = 0; i != MAXALIASES && g_aliases->alternatives[i].key; ++i) {
+ int j;
+ struct str_alias *alias = g_aliases->alternatives + i;
+ free(alias->key);
+ for (j = 0; j != MAXSTRINGS && alias->strings[j]; ++j) {
+ free(alias->strings[j]);
+ }
+ }
+ free(g_aliases);
+ g_aliases = anext;
+ }
+}
+
+str_aliases *get_aliases(const struct locale *lang) {
+ str_aliases *aliases = g_aliases;
+ while (aliases && aliases->lang != lang) {
+ aliases = aliases->next;
+ }
+ if (aliases == NULL) {
+ aliases = calloc(1, sizeof(struct str_aliases));
+ aliases->next = g_aliases;
+ aliases->lang = lang;
+ g_aliases = aliases;
+ }
+ return aliases;
+}
+
+void alias_add(struct str_aliases *aliases, const char *key, const char *text) {
+ int i, j;
+ struct str_alias *alias;
+
+ for (i = 0; i != MAXALIASES && aliases->alternatives[i].key; ++i) {
+ if (0 == strcmp(key, aliases->alternatives[i].key)) {
+ break;
+ }
+ }
+ assert(i != MAXALIASES);
+ alias = aliases->alternatives + i;
+ alias->key = str_strdup(key);
+ for (j = 0; j != MAXSTRINGS; ++j) {
+ if (alias->strings[j] == NULL) {
+ alias->strings[j] = str_strdup(text);
+ break;
+ }
+ }
+ assert(j != MAXSTRINGS);
+}
+
+const struct str_alias *alias_get(const struct locale *lang, const char *key)
+{
+ str_aliases *aliases = g_aliases;
+ while (aliases && aliases->lang != lang) {
+ aliases = aliases->next;
+ }
+ if (aliases) {
+ int i;
+ for (i = 0; i != MAXALIASES && aliases->alternatives[i].key; ++i) {
+ if (0 == strcmp(key, aliases->alternatives[i].key)) {
+ return aliases->alternatives + i;
+ }
+ }
+ }
+ return NULL;
+}
diff --git a/src/util/aliases.h b/src/util/aliases.h
new file mode 100644
index 000000000..634aeab3d
--- /dev/null
+++ b/src/util/aliases.h
@@ -0,0 +1,23 @@
+#pragma once
+
+struct locale;
+
+#define MAXSTRINGS 2
+#define MAXALIASES 16
+
+typedef struct str_alias {
+ char *key;
+ char *strings[MAXSTRINGS];
+} str_alias;
+
+typedef struct str_aliases {
+ struct str_aliases *next;
+ const struct locale *lang;
+ str_alias alternatives[MAXALIASES];
+} str_aliases;
+
+void free_aliases(void);
+
+str_aliases *get_aliases(const struct locale *lang);
+void alias_add(str_aliases *aliases, const char *key, const char *text);
+const str_alias *alias_get(const struct locale *lang, const char *key);
diff --git a/src/util/filereader.c b/src/util/filereader.c
deleted file mode 100644
index 5973c8f1c..000000000
--- a/src/util/filereader.c
+++ /dev/null
@@ -1,198 +0,0 @@
-#include
-#include "filereader.h"
-
-#include
-#include
-
-#include
-#include
-#include
-
-#define COMMENT_CHAR ';'
-#define CONTINUE_CHAR '\\'
-#define MAXLINE 4096*16
-static char lbuf[MAXLINE];
-static char fbuf[MAXLINE];
-
-static void unicode_warning(const char *bp)
-{
- log_warning("invalid sequence in UTF-8 string: %s\n", bp);
-}
-
-static int eatwhite(const char *ptr, size_t * total_size)
-{
- int ret = 0;
-
- *total_size = 0;
-
- while (*ptr) {
- wint_t wc;
- size_t size = 0;
- ret = unicode_utf8_decode(&wc, ptr, &size);
- if (ret != 0)
- break;
- if (!iswspace(wc))
- break;
- *total_size += size;
- ptr += size;
- }
- return ret;
-}
-
-static const char *getbuf_utf8(FILE * F)
-{
- bool cont = false;
- char quote = 0;
- bool comment = false;
- char *cp = fbuf;
- char *tail = lbuf + MAXLINE - 2;
-
- tail[1] = '@'; /* if this gets overwritten by fgets then the line was very long. */
- do {
- const char *bp = fgets(lbuf, MAXLINE, F);
- size_t white;
- if (bp == NULL) {
- return NULL;
- }
-
- eatwhite(bp, &white); /* decoding errors will get caught later on, don't have to check */
- bp += white;
-
- comment = (comment && cont);
- quote = (quote && cont);
-
- if (tail[1] == 0) {
- /* we read the maximum number of bytes! */
- if (tail[0] != '\n') {
- /* it wasn't enough space to finish the line, eat the rest */
- for (;;) {
- tail[1] = '@';
- bp = fgets(lbuf, MAXLINE, F);
- if (bp == NULL)
- return NULL;
- if (tail[1]) {
- /* read enough this time to end the line */
- break;
- }
- }
- comment = false;
- cont = false;
- bp = NULL;
- continue;
- }
- else {
- tail[1] = '@';
- }
- }
- cont = false;
- while (*bp && cp < fbuf + MAXLINE) {
- wint_t wc;
- size_t size;
- int ret;
-
- if (!quote) {
- while (*bp == COMMENT_CHAR) {
- /* comment begins. we need to keep going, to look for CONTINUE_CHAR */
- comment = true;
- ++bp;
- }
- }
-
- if (*bp == '\n' || *bp == '\r') {
- /* line breaks, shmine breaks */
- break;
- }
-
- if (*bp == '"' || *bp == '\'') {
- if (quote == *bp) {
- quote = 0;
- if (!comment && cp < fbuf + MAXLINE)
- *cp++ = *bp;
- ++bp;
- continue;
- }
- else if (!quote) {
- quote = *bp++;
- if (!comment && cp < fbuf + MAXLINE)
- *cp++ = quote;
- continue;
- }
- }
-
- ret = unicode_utf8_decode(&wc, bp, &size);
-
- if (ret != 0) {
- unicode_warning(bp);
- break;
- }
-
- if (iswspace(wc)) {
- if (!quote) {
- bp += size;
- ret = eatwhite(bp, &size);
- bp += size;
- if (!comment && *bp && *bp != COMMENT_CHAR && cp < fbuf + MAXLINE)
- *(cp++) = ' ';
- if (ret != 0) {
- unicode_warning(bp);
- break;
- }
- }
- else if (!comment) {
- if (cp + size <= fbuf + MAXLINE) {
- while (size--) {
- *(cp++) = *(bp++);
- }
- }
- else
- bp += size;
- }
- else {
- bp += size;
- }
- }
- else if (iswcntrl(wc)) {
- if (!comment && cp < fbuf + MAXLINE) {
- *cp++ = '?';
- }
- bp += size;
- }
- else {
- if (*bp == CONTINUE_CHAR) {
- const char *handle_end;
- eatwhite(bp + 1, &white);
- handle_end = bp + 1 + white;
- if (*handle_end == '\0') {
- bp = handle_end;
- cont = true;
- continue;
- }
- if (!comment && cp < fbuf + MAXLINE)
- *cp++ = *bp++;
- else
- ++bp;
- }
- else {
- if (!comment && cp + size <= fbuf + MAXLINE) {
- while (size--) {
- *(cp++) = *(bp++);
- }
- }
- else {
- bp += size;
- }
- }
- }
- }
- if (cp == fbuf + MAXLINE) {
- --cp;
- }
- *cp = 0;
- } while (cont || cp == fbuf);
- return fbuf;
-}
-
-const char *getbuf(FILE * F)
-{
- return getbuf_utf8(F);
-}
diff --git a/src/util/filereader.h b/src/util/filereader.h
deleted file mode 100644
index 53c24f697..000000000
--- a/src/util/filereader.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef UTIL_FILEREADER_H
-#define UTIL_FILEREADER_H
-
-#include
-#ifdef __cplusplus
-extern "C" {
-#endif
-
- const char *getbuf(FILE *);
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/src/util/keyword.c b/src/util/keyword.c
index 9d5ba1e6a..07b557572 100644
--- a/src/util/keyword.c
+++ b/src/util/keyword.c
@@ -61,7 +61,7 @@ keyword_t get_keyword(const char *s, const struct locale *lang) {
char buffer[64];
char *str = transliterate(buffer, sizeof(buffer) - sizeof(int), s);
- if (str) {
+ if (str && str[0] && str[1]) {
int i;
void *match;
void **tokens = get_translations(lang, UT_KEYWORDS);
@@ -155,5 +155,6 @@ const char *keywords[MAXKEYWORDS] = {
"pay",
"loot",
"autostudy",
+ "locale",
};
diff --git a/src/util/keyword.h b/src/util/keyword.h
index cc78fd6f7..0dcb44ce5 100644
--- a/src/util/keyword.h
+++ b/src/util/keyword.h
@@ -72,6 +72,7 @@ extern "C"
K_PAY,
K_LOOT,
K_AUTOSTUDY,
+ K_LOCALE,
MAXKEYWORDS,
NOKEYWORD
} keyword_t;
diff --git a/src/util/param.c b/src/util/param.c
index 0c486ce5a..38a33d322 100644
--- a/src/util/param.c
+++ b/src/util/param.c
@@ -67,7 +67,7 @@ param_t findparam(const char *s, const struct locale * lang)
char buffer[64];
char * str = s ? transliterate(buffer, sizeof(buffer) - sizeof(int), s) : 0;
- if (str && *str) {
+ if (str && str[0] && str[1]) {
int i;
void * match;
void **tokens = get_translations(lang, UT_PARAMS);
diff --git a/src/util/parser.c b/src/util/parser.c
index e0f6aeb67..36a4b1565 100644
--- a/src/util/parser.c
+++ b/src/util/parser.c
@@ -147,7 +147,7 @@ char *parse_token(const char **str, char *lbuf, size_t buflen)
char *cursor = lbuf;
char quotechar = 0;
bool escape = false;
- const char *ctoken = *str;
+ const char *ctoken = *str, *cstart;
if (!ctoken) {
return 0;
@@ -159,6 +159,7 @@ char *parse_token(const char **str, char *lbuf, size_t buflen)
}
return 0;
}
+ cstart = ctoken;
while (*ctoken) {
wint_t wc;
size_t len;
@@ -172,8 +173,15 @@ char *parse_token(const char **str, char *lbuf, size_t buflen)
else {
int ret = unicode_utf8_decode(&wc, ctoken, &len);
if (ret != 0) {
- log_warning("illegal character sequence in UTF8 string: %s\n", ctoken);
- break;
+ log_info("falling back to ISO-8859-1: %s\n", cstart);
+ if (cursor - buflen < lbuf - 2) {
+ size_t inlen = 1;
+ len = 2;
+ unicode_latin1_to_utf8(cursor, &len, ctoken, &inlen);
+ cursor += len;
+ ctoken += inlen;
+ continue;
+ }
}
}
if (escape) {
@@ -190,7 +198,7 @@ char *parse_token(const char **str, char *lbuf, size_t buflen)
++ctoken;
break;
}
- else if (quotechar == 0) {
+ else if (quotechar == 0 && cstart == ctoken) {
quotechar = utf8_character;
++ctoken;
}
diff --git a/src/util/parser.test.c b/src/util/parser.test.c
index 7690a01b4..27ce4e918 100644
--- a/src/util/parser.test.c
+++ b/src/util/parser.test.c
@@ -33,10 +33,48 @@ static void test_parse_token_bug_2381(CuTest *tc) {
char token[64];
stok = s;
- stok = parse_token(&stok, token, sizeof(token));
+ parse_token(&stok, token, sizeof(token));
CuAssertTrue(tc, strlen(token) < sizeof(token));
}
+static void test_parse_token_quotes(CuTest *tc) {
+ const char *stok, *s = "There are 'exactly four' \"tokens\"";
+ char token[64], *tok;
+
+ stok = s;
+ tok = parse_token(&stok, token, sizeof(token));
+ CuAssertStrEquals(tc, "There", token);
+ tok = parse_token(&stok, token, sizeof(token));
+ CuAssertStrEquals(tc, "are", tok);
+ tok = parse_token(&stok, token, sizeof(token));
+ CuAssertStrEquals(tc, "exactly four", tok);
+ tok = parse_token(&stok, token, sizeof(token));
+ CuAssertStrEquals(tc, "tokens", tok);
+ tok = parse_token(&stok, token, sizeof(token));
+ CuAssertPtrEquals(tc, NULL, (void *)tok);
+ CuAssertStrEquals(tc, "", token);
+}
+
+static void test_parse_token_quote_bug_turn_1179(CuTest *tc) {
+ const char *stok, *s = "O'Leary and \"O'Hara\" \"'nuff\" said'";
+ char token[64], *tok;
+
+ stok = s;
+ tok = parse_token(&stok, token, sizeof(token));
+ CuAssertStrEquals(tc, "O'Leary", tok);
+ tok = parse_token(&stok, token, sizeof(token));
+ CuAssertStrEquals(tc, "and", tok);
+ tok = parse_token(&stok, token, sizeof(token));
+ CuAssertStrEquals(tc, "O'Hara", tok);
+ tok = parse_token(&stok, token, sizeof(token));
+ CuAssertStrEquals(tc, "'nuff", tok);
+ tok = parse_token(&stok, token, sizeof(token));
+ CuAssertStrEquals(tc, "said'", tok);
+ tok = parse_token(&stok, token, sizeof(token));
+ CuAssertPtrEquals(tc, NULL, (void *)tok);
+ CuAssertStrEquals(tc, "", token);
+}
+
static void test_parse_token_limit(CuTest *tc) {
char lbuf[8];
const char *tok;
@@ -126,6 +164,8 @@ CuSuite *get_parser_suite(void)
SUITE_ADD_TEST(suite, test_atoip);
SUITE_ADD_TEST(suite, test_skip_token);
SUITE_ADD_TEST(suite, test_parse_token);
+ SUITE_ADD_TEST(suite, test_parse_token_quotes);
+ SUITE_ADD_TEST(suite, test_parse_token_quote_bug_turn_1179);
SUITE_ADD_TEST(suite, test_parse_token_bug_2381);
SUITE_ADD_TEST(suite, test_parse_token_limit);
SUITE_ADD_TEST(suite, test_parse_token_limit_utf8);
diff --git a/src/util/unicode.c b/src/util/unicode.c
index e87c27fc3..c7fada910 100644
--- a/src/util/unicode.c
+++ b/src/util/unicode.c
@@ -25,7 +25,7 @@
#define B00000001 0x01
static bool char_trimmed(wint_t wc) {
- if (wc >= 0x2000 && wc <= 0x200f) {
+ if (wc == 0xa0 || wc == 0x202f || (wc >= 0x2000 && wc <= 0x200f)) {
/* only weird stuff here */
return true;
}
diff --git a/src/util/unicode.test.c b/src/util/unicode.test.c
index 8a5d77488..f746c77da 100644
--- a/src/util/unicode.test.c
+++ b/src/util/unicode.test.c
@@ -154,7 +154,7 @@ static void test_unicode_compare(CuTest *tc)
}
static void test_unicode_trim_zwnj(CuTest *tc) {
- const char zwnj[] = { 0xe2, 0x80, 0x8c, 0x00 };
+ const char zwnj[] = { 0xe2, 0x80, 0x8c, 0 };
char name[64];
char expect[64];
snprintf(name, sizeof(name), "%sA%sB%s ", zwnj, zwnj, zwnj);
@@ -163,8 +163,38 @@ static void test_unicode_trim_zwnj(CuTest *tc) {
CuAssertStrEquals(tc, expect, name);
}
+static void test_unicode_trim_nbsp(CuTest *tc) {
+ const char code[] = { 0xc2, 0xa0, 0 };
+ char name[64];
+ char expect[64];
+ snprintf(name, sizeof(name), "%sA%sB%s ", code, code, code);
+ snprintf(expect, sizeof(expect), "A%sB", code);
+ CuAssertIntEquals(tc, 6, unicode_utf8_trim(name));
+ CuAssertStrEquals(tc, expect, name);
+}
+
+static void test_unicode_trim_nnbsp(CuTest *tc) {
+ const char code[] = { 0xe2, 0x80, 0xaf, 0 };
+ char name[64];
+ char expect[64];
+ snprintf(name, sizeof(name), "%sA%sB%s ", code, code, code);
+ snprintf(expect, sizeof(expect), "A%sB", code);
+ CuAssertIntEquals(tc, 8, unicode_utf8_trim(name));
+ CuAssertStrEquals(tc, expect, name);
+}
+
+static void test_unicode_trim_figure_space(CuTest *tc) {
+ const char code[] = { 0xe2, 0x80, 0x87, 0 };
+ char name[64];
+ char expect[64];
+ snprintf(name, sizeof(name), "%sA%sB%s ", code, code, code);
+ snprintf(expect, sizeof(expect), "A%sB", code);
+ CuAssertIntEquals(tc, 8, unicode_utf8_trim(name));
+ CuAssertStrEquals(tc, expect, name);
+}
+
static void test_unicode_trim_ltrm(CuTest *tc) {
- const char ltrm[] = { 0xe2, 0x80, 0x8e, 0x00 };
+ const char ltrm[] = { 0xe2, 0x80, 0x8e, 0 };
char name[64];
char expect[64];
snprintf(name, sizeof(name), "%sBrot%szeit%s ", ltrm, ltrm, ltrm);
@@ -188,6 +218,9 @@ CuSuite *get_unicode_suite(void)
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_unicode_trim);
SUITE_ADD_TEST(suite, test_unicode_trim_zwnj);
+ SUITE_ADD_TEST(suite, test_unicode_trim_nbsp);
+ SUITE_ADD_TEST(suite, test_unicode_trim_nnbsp);
+ SUITE_ADD_TEST(suite, test_unicode_trim_figure_space);
SUITE_ADD_TEST(suite, test_unicode_trim_ltrm);
SUITE_ADD_TEST(suite, test_unicode_trim_emoji);
SUITE_ADD_TEST(suite, test_unicode_utf8_to_other);
diff --git a/src/wormhole.c b/src/wormhole.c
index 22f2e3d8b..f4815f158 100644
--- a/src/wormhole.c
+++ b/src/wormhole.c
@@ -116,13 +116,13 @@ static attrib_type at_wormhole = {
static void
make_wormhole(const building_type * bt_wormhole, region * r1, region * r2)
{
- building *b1 = new_building(bt_wormhole, r1, default_locale);
- building *b2 = new_building(bt_wormhole, r2, default_locale);
+ int size = bt_wormhole->maxcapacity * bt_wormhole->capacity;
+ building *b1 = new_building(bt_wormhole, r1, default_locale, size);
+ building *b2 = new_building(bt_wormhole, r2, default_locale, size);
attrib *a1 = a_add(&b1->attribs, a_new(&at_wormhole));
attrib *a2 = a_add(&b2->attribs, a_new(&at_wormhole));
a1->data.v = b2->region;
a2->data.v = b1->region;
- b1->size = b2->size = bt_wormhole->maxcapacity * bt_wormhole->capacity;
ADDMSG(&r1->msgs, msg_message("wormhole_appear", "region", r1));
ADDMSG(&r2->msgs, msg_message("wormhole_appear", "region", r2));
}
diff --git a/src/wormhole.test.c b/src/wormhole.test.c
index 4b6fe2d3c..6d531f214 100644
--- a/src/wormhole.test.c
+++ b/src/wormhole.test.c
@@ -37,6 +37,8 @@ static void test_make_wormholes(CuTest *tc) {
test_setup();
setup_wormholes();
btype = test_create_buildingtype("wormhole");
+ btype->maxsize = 4;
+ btype->maxcapacity = 4;
match[0] = r1 = test_create_plain(0, 0);
match[1] = r2 = test_create_plain(1, 0);
make_wormholes(match, 2, btype);