diff --git a/.gitignore b/.gitignore
index 191be8658..39abdc995 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,3 +27,4 @@ game-e3/reports/
*.log
*.log.*
tags
+Thumbs.db
diff --git a/core/scripts/init.lua b/core/scripts/init.lua
index 83bcc4f63..b184887c3 100755
--- a/core/scripts/init.lua
+++ b/core/scripts/init.lua
@@ -4,7 +4,7 @@ require "resources"
function run_editor()
local turn = get_turn()
- if turn==0 then
+ if turn<0 then
turn = read_turn()
set_turn(turn)
end
@@ -28,7 +28,7 @@ function run_turn()
require(config.game .. ".main")
local turn = get_turn()
- if turn==0 then
+ if turn<0 then
turn = read_turn()
set_turn(turn)
end
diff --git a/core/scripts/multis.lua b/core/scripts/multis.lua
index ad0b77783..e7157a40f 100644
--- a/core/scripts/multis.lua
+++ b/core/scripts/multis.lua
@@ -1,6 +1,6 @@
function kill_nonstarters()
for f in factions() do
- if f.lastturn==1 then
+ if f.lastturn==0 then
kill_faction(f, true)
end
end
diff --git a/doc/chapters.md b/doc/chapters.md
new file mode 100644
index 000000000..02ddeec0d
--- /dev/null
+++ b/doc/chapters.md
@@ -0,0 +1,28 @@
+# Eressea Rules
+
+© Copyright Enno Rehling 2014
+
+## Contents
+
+1. Introduction
+2. First Turn by Example
+3. The Economy: Recruiting, Upkeep and Silver
+4. Studying Skills
+5. Movement and Seafaring
+6. Combat
+7. Alliances and Groups
+7. Resources and Production
+8. Magic and Alchemy
+9. Castles and other buildings
+10. Races
+11. List of Orders
+12. List of Skills
+13. List of Buildings
+14. List of Items
+15. Appendices and Tables
+
+## Acknowledgments
+
+This game would not be possible without the work of Russell Wallace, who created the Atlantis PBEM in 1993. I would also like to thank Alex Schröder, who wrote the first German translation of Atlantis. Christian Schlittchen, Katja Zedel and Henning Peters started Eressea, and invited me to be a part of their team and become their friends in the process. Ingo Wilken, Benjamin Bärmann, Stefan Reich, and Martin Hershoff have contributed code and valuable design input to the server. Many advanced features are a direct result of entertaining debates on the eressea-kom and atlantisdev discussion lists, and I would like to thank them and the entire community for their support and encouragement over the years.
+
+Enno Rehling, April 2014
diff --git a/doc/introduction.md b/doc/introduction.md
new file mode 100644
index 000000000..e21374a84
--- /dev/null
+++ b/doc/introduction.md
@@ -0,0 +1,79 @@
+# Introduction
+
+Eressea is a play-by-email strategy role-playing game that simulates a fantasy world. Unlike most video games, there are are no fancy graphics, but everything that happens is communicated by text.
+
+As a player, you are in control of a number of characters, which are referred to as units, which combine to make a faction. Your faction competes with other factions over the resources of the world that you explore together. Sometimes this leads to war, and sometimes to friendship between factions, and eventually it all adds up to a rich history of the world, with its own heroes and legends.
+
+Eressea is different from most games in many respects, but most of all in that there is no declared winner. There is not even a goal, other than to enjoy yourself, and set your own goals. Do you want to monopolize a natural resource? Become the strongest military force? Create a democratic government for the factions in your part of the world? Collect trophies from slain dragons? Map the entire globe? Those are all valid goals to set for yourself, but you might find your own.
+
+### How to play
+
+Eressea is played in weekly turns. Every weekend, you receive a textual report containing your units, the part of the world that they can observe, and any events that happened since the last report.
+
+Based on that report, players send an email to the game's server that contains orders for all of their units. A week later, the server evaluates the orders from all players, updates the state of the world, and sends out new reports.
+
+A (slightly simplified) report may look like this:
+
+ Report for Eressea, Sunday, 13. April 2014, 23:25
+
+ It is the first week of the month of harvest moon in
+ the 1. year of the fourth age. It is summer.
+
+ Guardians (xin8), elves/draig (guardian@gmx.de)
+
+ Your faction has 4 people in 1 unit.
+
+ Vetkan (10,1), highland, 1012 peasants, 13156 silver,
+ 17 horses. To the northwest lies the forests of
+ Buviken (9,2), to the northeast the mountains of Rebus
+ (10,2), to the east the plain of Setutvul (-9,1), to
+ the southeast the mountains of Vithil (-9,0), to the
+ southwest the forests of Posotmetid (10,0) and to the
+ west the forests of Cesartapun (9,1)
+
+ The local market offers silk and mandrakes.
+
+ Statistics for Vetkan (10,1):
+
+ Peasant wages: 11 silver
+ Recruits: 25 peasants
+
+ Pelenth (opw4), size 10, fortification.
+
+ * Fighters of Pelenth (r6b0), 4 elves,
+ aggressive, skills: melee 5, has: platemail,
+ sword, 500 silver.
+
+ + Osswid the Destroyer (wtmj), Vikings (atnf), 1
+ dwarf, has: 1 iron.
+
+What you see here are two units, Osswid (wtmj) and the Fighters of Pelenth (r6b0). The fighters are our own unit, but Osswid belongs to the faction Vikings (atnf). Every unit, faction or other object in the game is identified by a four-letter code, and it is common to append it to the name in parentheses. The fort of Pelenth (opw4) is a building, and our fighters are inside, which causes them to be indented a little more, and gives them some benefits including additional protection in combat.
+
+While Osswid is a single dwarf, our unit of fighters consists of several elves. Units can any number of men, which are always of the same race. Each unit can learn any number of skills, and our Fighters are skilled in melee combat at a respectable level 5. They own a sword and one platemail, which means that one of them will be well equipped when going into battle, while the other three will fight with their bare hands. The unit also has 500 pieces of silver, the currency of the game.
+
+Since he isn't of our own faction, we cannot tell what skills Osswid has, but we can tell that he's unarmed.
+
+We see all this because one of our units is in the region, the highland of Vetkan (10,1). Regions do not have a four-letter identifier, but are instead identified by their coordinates on the map, which makes it easier to visualize the world.
+
+The region of Vetkan is surrounded by a number of other regions on six sides, since Eressea's Map uses a hexagonal grid. Every region has a terrain, and we can see highland, mountain, plain and forest regions in our vicinity. Each terrain has different qualities: For example, mountains are good for mining iron ore and quarries, while plains are fertile and forests produce lumber. Each region is inhabited by a peasant population, and you can see that Vetkan has a population of 1012. These peasants are the basis of the region's economy, and they own a combined 13,156 silver that we could try to acquire through taxation or other means.
+
+Example orders for this faction may look like this:
+
+ ERESSEA xin8 "password"
+ UNIT r6b0
+ STUDY perception
+ GIVE wtmj 100 silver
+ GIVE TEMP 1 150 silver
+ GIVE TEMP 2 150 silver
+ MAKE TEMP 1
+ RECRUIT 1
+ MOVE W
+ END
+ MAKE TEMP 2
+ RECRUIT 1
+ MOVE E
+ END
+
+Every set of orders begins with the faction's id and password, and is followed by orders for one or more units. A unit's orders begin with the word UNIT followed by the unit's id, and one or more orders. In this case, we are giving the unit an order to STUDY the perception skill, and to GIVE silver to three other units. STUDY is a standard action, and each unit can perform exactly one per turn. If you do not specify a standard action, then the unit will usually repeat the action from the previous week. GIVE is a free action, and all units can perform as many free actions in a week as they like.
+
+The MAKE TEMP order is special: It creates a new unit with a temporary id (in this case, we're calling them TEMP 1 and TEMP 2). The server will replace this with a unique id by the time it sends the next report, but until then, we can address the new unit by this temporary identifier, for example to give it the silver that it requires for recruiting new members. RECRUIT is another example of a free action, while MOVE is a standard action. The MAKE TEMP order is technically given by the unit whose orders preceded it, and the new unit's orders are finished with the END keyword.
diff --git a/res/catalog-e3a.xml b/game-e2/catalog.xml
similarity index 56%
rename from res/catalog-e3a.xml
rename to game-e2/catalog.xml
index 96dbf8d9d..a6c2882c8 100644
--- a/res/catalog-e3a.xml
+++ b/game-e2/catalog.xml
@@ -5,6 +5,12 @@
+
+
diff --git a/res/config-eressea.xml b/game-e2/config.xml
similarity index 64%
rename from res/config-eressea.xml
rename to game-e2/config.xml
index d06464759..72c1563ab 100644
--- a/res/config-eressea.xml
+++ b/game-e2/config.xml
@@ -1,36 +1,37 @@
-
+
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
@@ -43,11 +44,11 @@
-
-
-
-
-
+
+
+
+
+
diff --git a/game-e2/eressea.ini b/game-e2/eressea.ini
index 8e4eff430..f92fcd87e 100644
--- a/game-e2/eressea.ini
+++ b/game-e2/eressea.ini
@@ -1,6 +1,5 @@
[eressea]
base = .
-load = setup.lua
report = reports
verbose = 0
lomem = 0
diff --git a/game-e2/runtests.lua b/game-e2/runtests.lua
new file mode 100644
index 000000000..423094391
--- /dev/null
+++ b/game-e2/runtests.lua
@@ -0,0 +1,2 @@
+require "setup"
+run_tests()
diff --git a/game-e2/setup.lua b/game-e2/setup.lua
index d9dea3541..ccd43719d 100644
--- a/game-e2/setup.lua
+++ b/game-e2/setup.lua
@@ -10,6 +10,5 @@ for idx, path in pairs(paths) do
package.path = srcpath .. '/' .. path .. ';' .. package.path
end
-read_xml(respath..'/config-eressea.xml', respath..'/catalog-eressea.xml')
-
+assert(read_xml())
require "init"
diff --git a/game-e3/catalog.xml b/game-e3/catalog.xml
index 93cda3505..3d82919cd 100644
--- a/game-e3/catalog.xml
+++ b/game-e3/catalog.xml
@@ -4,13 +4,13 @@
"http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd">
-
+
diff --git a/game-e3/runtests.lua b/game-e3/runtests.lua
new file mode 100644
index 000000000..423094391
--- /dev/null
+++ b/game-e3/runtests.lua
@@ -0,0 +1,2 @@
+require "setup"
+run_tests()
diff --git a/game-e3/setup.lua b/game-e3/setup.lua
index e93f64a48..ccd43719d 100644
--- a/game-e3/setup.lua
+++ b/game-e3/setup.lua
@@ -10,6 +10,5 @@ for idx, path in pairs(paths) do
package.path = srcpath .. '/' .. path .. ';' .. package.path
end
-read_xml(respath..'/config-e3a.xml', respath..'/catalog-e3a.xml')
-
+assert(read_xml())
require "init"
diff --git a/process/compress.py b/process/compress.py
new file mode 100755
index 000000000..539f1a032
--- /dev/null
+++ b/process/compress.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+
+from sys import argv, exit
+from string import join
+from os import access, R_OK
+from os import system
+
+gamename='Eressea'
+
+if(len(argv) >= 3):
+ gamename=argv[2]
+
+template="""#!/bin/bash
+#PATH=$PATH:$HOME/bin
+
+addr=%(email)s
+[ $# -ge 1 ] && addr=$1
+[ -z $addr ] || send-%(compression)s-report $addr '%(gamename)s Report #%(turn)s' %(files)s
+"""
+
+turn = argv[1]
+try:
+ infile = file("reports.txt", "r")
+except:
+ print "%s: reports.txt file does not exist" % (argv[0], )
+ exit(0)
+
+for line in infile.readlines():
+ settings = line[:-1].split(":")
+ options = { "turn" : turn}
+ options["gamename"] = gamename
+ for setting in settings:
+ try:
+ key, value = setting.split("=")
+ options[key] = value
+ except:
+ print "Invalid input line", line
+ if not options.has_key("reports"):
+ continue
+ reports = options["reports"].split(",")
+# reports = reports + [ "iso.cr" ]
+ prefix = "%(turn)s-%(faction)s." % options
+ files=[]
+ if options["compression"]=="zip":
+ output = prefix+"zip"
+ files = [output]
+ if (access(output, R_OK)):
+ pass
+ else:
+ parameters = []
+ for extension in reports:
+ filename = "%s%s" % (prefix, extension)
+ if (access(filename, R_OK)):
+ parameters = parameters + [ filename ]
+ system("zip %s -q -m -j -1 %s" % (output, join(parameters," ")))
+ else:
+ for extension in reports:
+ if extension!='':
+ filename = "%s%s" % (prefix, extension)
+ output = "%s%s.bz2" % (prefix, extension)
+ files = files+[output]
+ if access(filename, R_OK):
+ if (access(output, R_OK)):
+ #print output, "exists, skipping"
+ continue
+ system("bzip2 %s" % filename)
+ #print files
+ options["files"] = join(files, " ")
+ batch = file("%s.sh" % options["faction"], "w")
+ batch.write(template % options)
+ batch.close()
+infile.close()
diff --git a/process/orders-accept b/process/orders-accept
new file mode 100755
index 000000000..552a3e9a1
--- /dev/null
+++ b/process/orders-accept
@@ -0,0 +1,2 @@
+#/bin/.sh
+grep -v '>From' | $HOME/src/scripts/bin/orders-accept $*
diff --git a/process/orders-process b/process/orders-process
new file mode 100755
index 000000000..d15d188cf
--- /dev/null
+++ b/process/orders-process
@@ -0,0 +1,218 @@
+#!/usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+
+from os import unlink, symlink, rename, popen, tmpfile
+from os.path import exists
+from re import compile, IGNORECASE
+from string import split, join, upper, strip
+from sys import argv, exit
+from time import sleep, time, ctime
+from syslog import openlog, closelog, syslog
+
+from epasswd import EPasswd
+
+def pwd_get_email(faction, pwd, pwdfile=None):
+ return None
+
+def splitfilename(filename):
+ from os.path import split
+ return split(filename)
+
+def unlock_file(filename):
+ try:
+ unlink(filename+".lock")
+ except:
+ print "could not unlock %s.lock, file not found" % filename
+ raise
+
+def lock_file(filename):
+ i = 0
+ wait = 1
+ if not exists(filename):
+ file=open(filename, "w")
+ file.close()
+ while True:
+ try:
+ symlink(filename, filename+".lock")
+ return
+ except:
+ i = i+1
+ if i == 5:
+ raise
+ sleep(wait)
+ wait = wait*2
+
+messages = {
+"subject-de": "Befehle angekommen",
+"subject-en": "orders received",
+
+"validate-en": "Validating",
+"validate-de": "Verarbeite",
+
+"faction-en": "Faction",
+"faction-de": "Partei",
+
+"unknown-de": "WARNUNG: Die Partei ist nicht bekannt, oder das Passwort falsch!",
+"unknown-en": "WARNING: This faction is unknown, or the password is incorrect!",
+
+"warning-de": "Warnung",
+"warning-en": "Warning",
+
+"error-de": "Fehler",
+"error-en": "Error",
+}
+
+
+# base directory for all your games:
+rootdir = "/home/eressea/eressea"
+frommail = "Eressea Server "
+orderbase = "orders.dir"
+sendmail = True
+maxlines = 25
+echeck_cmd = "/home/eressea/echeck/echeck.sh"
+
+# regular expression that finds the start of a faction
+fact_re = compile("^\s*(eressea|vinyambar|partei|faction)\s+([a-zA-Z0-9]+)\s+\"?([^\"]*)\"?", IGNORECASE)
+
+def check_pwd(filename, email, pw_data):
+ results = []
+ try:
+ file = open(filename, "r")
+ except:
+ print "could not open file", filename
+ return results
+ for line in file.readlines():
+ mo = fact_re.search(strip(line))
+ if mo != None:
+ fact_nr = str(mo.group(2))
+ fact_pw = str(mo.group(3))
+ if pw_data.fac_exists(fact_nr):
+ if pw_data.check(fact_nr, fact_pw) == 0:
+ 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) ]
+ else:
+ results = results + [ (fact_nr, None, False, fact_pw) ]
+ return results
+
+def echeck(filename, locale, rules):
+ dirname, filename = splitfilename(filename)
+ stream = popen("%s %s %s %s %s" % (echeck_cmd, locale, filename, dirname, rules), 'r')
+ lines = stream.readlines()
+ if len(lines)==0:
+ stream.close()
+ return None
+ if len(lines)>maxlines:
+ mail = join(lines[:maxlines-3] + ["...", "\n"] + lines[-3:], '')
+ else:
+ mail = join(lines[:maxlines], '')
+ stream.close()
+ return mail
+
+## the main body of the script
+game = int(argv[1])
+
+basedir = rootdir + "/game-%d" % (game, )
+queuename = basedir + "/orders.queue"
+if not exists(queuename):
+ exit(0)
+
+# parse the queue file -
+#print "connecting to SMTP..."
+from smtplib import SMTP
+try:
+ server = SMTP("localhost")
+except:
+ print "could not connect to SMTP server"
+ exit(0)
+#print "reading password file..."
+pw_data = EPasswd(basedir + "/passwd")
+
+#print "reading orders.queue..."
+# move the queue file to a save space while locking it:
+try:
+ lock_file(queuename)
+except:
+ exit(0)
+queuefile = open(queuename, "r")
+lines = queuefile.readlines()
+queuefile.close()
+
+# copy to a temp file
+
+tname="/tmp/orders.queue.%s" % str(time())
+try:
+ lock_file(tname)
+except:
+ exit(0)
+tmpfile=open(tname, "w")
+for line in lines:
+ tmpfile.write(line)
+tmpfile.close()
+
+openlog("orders")
+
+unlink(queuename)
+try:
+ unlock_file(queuename)
+except:
+ pass
+
+for line in lines:
+ tokens = split(line[:-1], ' ')
+ dict = {}
+ for token in tokens:
+ name, value = split(token, '=')
+ dict[name] = value
+
+ email = dict["email"]
+ locale = dict["locale"]
+ game = int(dict["game"])
+ file = dict["file"]
+ gamename='[E%d]' % game
+ rules='e%d' % game
+ warning = ""
+ failed = True
+ results = check_pwd(file, email, pw_data)
+ logfile = open(basedir+"/zug.log", "a")
+ dirname, filename = splitfilename(file)
+ msg = messages["validate-"+locale] + " " + filename + "\n\n"
+ for faction, game_email, success, pwd in results:
+ msg = msg + messages["faction-"+locale] + " " + faction + "\n"
+ if success: failed = False
+ else: msg = msg + messages["unknown-"+locale] + "\n"
+ msg = msg + "\n"
+ logfile.write("%s:%s:%s:%s:%s:%s\n" % (ctime(time()), email, game_email, faction, pwd, success))
+ logfile.close()
+
+ if failed:
+ warning = " (" + messages["warning-" + locale] + ")"
+ syslog("failed - no valid password in " + file)
+ else:
+ result = echeck(file, locale, rules)
+ if email=='eressea':
+ print result
+ continue
+ elif result is None:
+ # echeck did not finish
+ msg = msg + "Echeck was killed. Your turn was accepted, but could not be verified.\n"
+ warning = " (" + messages["warning-" + locale] + ")"
+ syslog("process - echeck got killed, " + file)
+ else:
+ msg = msg + result
+ syslog("process - checked orders in " + file)
+
+ subject = gamename + " " + messages["subject-" + locale] + warning
+ msg = "Subject: %s\nFrom: %s\nTo: %s\nContent-Type: text/plain; charset=utf-8\n\n" % (subject, frommail, email) + msg
+ try:
+ server.sendmail(frommail, email, msg)
+ except:
+ syslog("failed - cannot send to " + email)
+
+server.close()
+
+closelog()
+unlink(tname)
+unlock_file(tname)
diff --git a/process/orders.cron b/process/orders.cron
new file mode 100755
index 000000000..be573fe86
--- /dev/null
+++ b/process/orders.cron
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+## this script processes incoming order files.
+# files are delivered into an incoming queue by procmail, then cron runs
+# this here script to make a non-blocking syntax check and reject or
+# accept the order file.
+
+for GAME in $*
+do
+ if [ "$GAME" == "eressea" ]; then GAME=2 ; fi
+ if [ "$GAME" == "e3a" ]; then GAME=3 ; fi
+ if [ -e $HOME/eressea/game-$GAME/orders.queue ]
+ then
+ $HOME/bin/orders-process $GAME
+ fi
+done
diff --git a/process/send-bz2-report b/process/send-bz2-report
new file mode 100755
index 000000000..733b5c6be
--- /dev/null
+++ b/process/send-bz2-report
@@ -0,0 +1,35 @@
+#!/bin/bash
+if [ -z $ERESSEA ]; then
+ echo "You have to define the \$ERESSEA environment variable to run $0"
+ exit -2
+fi
+source $HOME/bin/functions.sh
+source $ERESSEA/etc/eressea.conf
+
+TEMPLATE=report-mail.txt
+if [ "$1" == "-Lde" ]
+then
+ TEMPLATE=report-mail.de.txt
+ shift
+fi
+
+if [ "$1" == "-Lde" ]
+then
+ TEMPLATE=report-mail.en.txt
+ shift
+fi
+
+EMAIL=$1
+SUBJECT=$2
+shift 2
+
+ATTACHMENTS=""
+while [ $# -gt 0 ]
+do
+ if [ -e "$1" ]; then
+ ATTACHMENTS="-a $1 $ATTACHMENTS"
+ fi
+ shift
+done
+
+cat $ERESSEA/etc/$TEMPLATE | mutt -F $ERESSEA/etc/muttrc -s "$SUBJECT" $ATTACHMENTS -- $EMAIL
diff --git a/process/send-zip-report b/process/send-zip-report
new file mode 100755
index 000000000..fb068c33f
--- /dev/null
+++ b/process/send-zip-report
@@ -0,0 +1,46 @@
+#!/bin/bash
+if [ -z $ERESSEA ]; then
+ ERESSEA=`echo $PWD |sed -e 's/\/game.*//'`
+ echo "Assuming that ERESSEA=$ERESSEA"
+fi
+if [ ! -f reports.txt ]; then
+ echo "need to run $0 from the report direcory"
+ exit -2
+fi
+
+PWD=$(pwd)
+GAME=$(dirname $PWD)
+
+TEMPLATE=report-mail.txt
+if [ "$1" == "-Lde" ]
+then
+ TEMPLATE=report-mail.de.txt
+ shift
+fi
+
+if [ "$1" == "-Len" ]
+then
+ TEMPLATE=report-mail.en.txt
+ shift
+fi
+
+if [ -e $GAME/$TEMPLATE ]; then
+TEMPLATE=$GAME/$TEMPLATE
+else
+TEMPLATE=$ERESSEA/etc/$TEMPLATE
+fi
+
+if [ ! -e $TEMPLATE ]; then
+ echo "no such email template: $TEMPLATE"
+ exit -3
+fi
+
+while [ -e /tmp/.stopped ] ; do
+ echo "waiting 2 minutes for lockfile in /tmp/.stopped to clear"
+ sleep 120
+done
+mutt -F $ERESSEA/etc/muttrc -s "$2" -a "$3" -- $1 < $TEMPLATE
+
+if [ $? -ne 0 ] ; then
+ echo "Sending failed for email/report: $2/$3"
+fi
diff --git a/res/adamantium.xml b/res/adamantium.xml
new file mode 100644
index 000000000..315a8d66c
--- /dev/null
+++ b/res/adamantium.xml
@@ -0,0 +1,36 @@
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
diff --git a/res/catalog-eressea.xml b/res/catalog-eressea.xml
deleted file mode 100644
index 96dbf8d9d..000000000
--- a/res/catalog-eressea.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
diff --git a/res/config-e3a.xml b/res/config-e3a.xml
deleted file mode 100644
index 07a3b8eab..000000000
--- a/res/config-e3a.xml
+++ /dev/null
@@ -1,189 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- eressea-server@eressea.de
- eressea-server@eressea.de
-
-
- Bitte denke daran, deine Befehle mit dem Betreff
- E3 BEFEHLE an eressea-server@eressea.de zu senden.
- Remember to send your orders to
- eressea-server@eressea.de with the subject E3 ORDERS.
-
-
- E3 BEFEHLE
- E3 ORDERS
-
-
-
diff --git a/res/e3a/strings.xml b/res/e3a/strings.xml
index 3b368a28e..9e6e83bd8 100644
--- a/res/e3a/strings.xml
+++ b/res/e3a/strings.xml
@@ -7,6 +7,37 @@
_a: including article (ein Troll, a troll)
-->
+
+ Adamantium
+ adamantium
+
+
+ Adamantium
+ adamantium
+
+
+ Adamantium
+ adamantium
+
+
+
+ Adamantiumaxt
+ adamantium axe
+
+
+ Adamantiumäxte
+ adamantium axes
+
+
+
+ Adamantiumrüstung
+ adamantium plate
+
+
+ Adamantiumrüstungen
+ adamantium plates
+
+
des dritten Zeitalters
diff --git a/res/e4/main.lua b/res/e4/main.lua
new file mode 100644
index 000000000..813e0f55a
--- /dev/null
+++ b/res/e4/main.lua
@@ -0,0 +1,61 @@
+require "multis"
+require "e3a.frost"
+
+function process(orders)
+ local confirmed_multis = { }
+ local suspected_multis = { }
+
+ if open_game(get_turn())~=0 then
+ print("could not read game")
+ return -1
+ end
+ init_summary()
+
+ -- run the turn:
+ if read_orders(orders) ~= 0 then
+ print("could not read " .. orders)
+ return -1
+ end
+
+ -- plan_monsters()
+ local mon = get_faction(666)
+ if mon ~= nil then
+ mon.lastturn = get_turn()
+ end
+
+ if nmr_check(config.maxnmrs or 30)~=0 then
+ return -1
+ end
+
+ process_orders()
+ if xmas2009~=nil then
+ xmas2009()
+ end
+
+ -- create new monsters:
+ spawn_dragons()
+ spawn_undead()
+
+ if get_turn()>=config.kill_after then
+ kill_nonstarters()
+ end
+ -- post-turn updates:
+ update_guards()
+ update_scores()
+ frost.update()
+
+ local localechange = { en = { "L46o" } }
+ change_locales(localechange)
+
+ -- use newfactions file to place out new players
+ -- autoseed(config.basepath .. "/newfactions", false)
+
+ write_files(config.locales)
+
+ file = "" .. get_turn() .. ".dat"
+ if eressea.write_game(file)~=0 then
+ print("could not write game")
+ return -1
+ end
+ return 0
+end
diff --git a/res/e4/modules.lua b/res/e4/modules.lua
new file mode 100644
index 000000000..39a1d7ca0
--- /dev/null
+++ b/res/e4/modules.lua
@@ -0,0 +1,4 @@
+require "spells"
+require "e3a.rules"
+require "e3a.markets"
+require "e3a.frost"
diff --git a/res/eressea/items.xml b/res/eressea/items.xml
index a1efc9620..8fe36ee1b 100644
--- a/res/eressea/items.xml
+++ b/res/eressea/items.xml
@@ -164,36 +164,4 @@
-
- -
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
diff --git a/s/runtests b/s/runtests
index 1ad5a0b0a..c56619db1 100755
--- a/s/runtests
+++ b/s/runtests
@@ -18,7 +18,7 @@ fi
echo $ROOT
$ROOT/$BIN_DIR/eressea/test_eressea
pushd $ROOT/game-e2
-$ROOT/$BIN_DIR/eressea/eressea -e run_tests
+$ROOT/$BIN_DIR/eressea/eressea runtests.lua
cd $ROOT/game-e3
-$ROOT/$BIN_DIR/eressea/eressea -e run_tests
+$ROOT/$BIN_DIR/eressea/eressea runtests.lua
popd
diff --git a/scripts/e3a/rules.lua b/scripts/e3a/rules.lua
index 2d137d2d0..0c5e9be85 100644
--- a/scripts/e3a/rules.lua
+++ b/scripts/e3a/rules.lua
@@ -8,7 +8,7 @@ function item_canuse(u, iname)
end
end
if iname=="rep_crossbow" then
- -- only dwarves and halflings allowed to use towershield
+ -- only dwarves and halflings allowed to use repeating crossbow
return race=="dwarf" or race=="halfling"
end
if iname=="scale" then
diff --git a/scripts/setup.lua b/scripts/setup.lua
new file mode 100644
index 000000000..116ee94d4
--- /dev/null
+++ b/scripts/setup.lua
@@ -0,0 +1,15 @@
+local srcpath = config.source_dir
+local respath = srcpath .. '/res'
+local paths = {
+ 'scripts/?.lua',
+ 'core/scripts/?.lua',
+ 'lunit/?.lua'
+}
+
+for idx, path in pairs(paths) do
+ package.path = srcpath .. '/' .. path .. ';' .. package.path
+end
+
+read_xml()
+
+require "init"
diff --git a/scripts/tools/build.lua b/scripts/tools/build.lua
new file mode 100644
index 000000000..1e8c6f5d5
--- /dev/null
+++ b/scripts/tools/build.lua
@@ -0,0 +1,100 @@
+function new_faction(email, race, lang, r)
+ f = faction.create(email, race, lang)
+ u = unit.create(f, r, 10)
+ u:add_item("log", 5);
+ u:add_item("horse", 2);
+ u:add_item("silver", 1000);
+ u:add_item("adamantium", 1);
+end
+
+function get_homes(f)
+ homes={}
+ for u in f.units do
+ table.insert(homes, u.region)
+ end
+ return homes
+end
+
+if eressea~=nil then
+ eressea.free_game()
+ eressea.read_game("game4.dat")
+ homes = get_homes(get_faction("xin8"))
+else
+ -- running in the lua interpreter, not eressea. fake it.
+ new_faction = print
+ eressea = { ['write_game'] = function(s) print("writing " .. s) end }
+ homes = { "Andune", "Bedap", "Curtis", "Dovre" }
+end
+
+local f=assert(io.open("factions", "r"))
+line=f:read("*line")
+players = {}
+emails = {}
+patrons = {}
+nplayers = 0
+while line~=nil do
+ fields = {}
+ line:gsub("([^\t]*)\t*", function(c) table.insert(fields, c) end)
+ line=f:read("*line")
+ email = fields[1]
+ if fields[2]=='yes' then
+ table.insert(patrons, email)
+ else
+ table.insert(emails, email)
+ end
+ if fields[3]=='German' then lang='de' else lang='en' end
+ race=string.gsub(fields[4], "/.*", ''):lower()
+ players[email] = { ['lang'] = lang, ['race'] = race }
+ nplayers = nplayers + 1
+end
+
+for k, r in ipairs(homes) do
+ print(k, r)
+end
+npatrons = #patrons
+print(#homes .. " regions.")
+print(nplayers .. " players.")
+print(npatrons .. " patrons.")
+
+maxfactions = 20
+selected = {}
+if maxfactions > nplayers then maxfactions = nplayers end
+while maxfactions > 0 do
+ if npatrons > 0 then
+ email = patrons[npatrons]
+ patrons[npatrons] = nil
+ npatrons = npatrons - 1
+ else
+ local np = #emails
+ local i = math.random(np)
+ email = emails[i]
+ emails[i] = emails[np]
+ emails[np] = nil
+ end
+ local player = players[email]
+ player.email = email
+ table.insert(selected, player)
+ maxfactions = maxfactions - 1
+end
+
+-- random shuffle
+for j=1,#selected do
+ k = math.random(j)
+ if k ~= j then
+ local temp = selected[j]
+ selected[j] = selected[k]
+ selected[k] = temp
+ end
+end
+
+print('## players')
+for k, player in ipairs(selected) do
+ local r = homes[1 + k % #homes]
+ new_faction(player.email, player.race, player.lang, r)
+ print(player.email)
+end
+eressea.write_game("game4.dat")
+print("## no faction")
+for i, email in ipairs(emails) do
+ print(email)
+end
diff --git a/scripts/tools/oceanfill.lua b/scripts/tools/oceanfill.lua
new file mode 100644
index 000000000..8025897f6
--- /dev/null
+++ b/scripts/tools/oceanfill.lua
@@ -0,0 +1,11 @@
+p = plane.get(0)
+w, h = p:size()
+print(p, w, h)
+for x=0,w-1 do
+ for y=0,h-1 do
+ r = get_region(x,y)
+ if r==nil then
+ r = region.create(x, y, "ocean")
+ end
+ end
+end
diff --git a/scripts/tools/reimburse.lua b/scripts/tools/reimburse.lua
new file mode 100644
index 000000000..80838fb83
--- /dev/null
+++ b/scripts/tools/reimburse.lua
@@ -0,0 +1,37 @@
+function main()
+ for f in factions() do
+ if f.race=="demon" then
+ f.flags = 2147484672
+ for u in f.units do
+ u.building.size = 2
+ u.building.name = u.region.name .. " Keep"
+ u.name = "Lord " .. u.region.name
+ u:add_item("money", 1000-u:get_item("money"))
+ end
+ else
+ u = f.units()
+ u:add_item("money", 1000-u:get_item("money"))
+ u:add_item("adamantium", 1-u:get_item("adamantium"))
+ end
+ end
+ for r in regions() do for u in r.units do
+ print(u)
+ things = ""
+ comma = ""
+ for i in u.items do
+ things = things .. comma .. u:get_item(i) .. " " .. i
+ comma = ", "
+ end
+ print(' - ' .. things)
+ end end
+end
+
+if eressea==nil then
+ print("this script is part of eressea")
+else
+ read_xml()
+ eressea.read_game('0.dat')
+ main()
+ eressea.write_game('0.dat')
+ print('done')
+end
diff --git a/scripts/tools/reports.lua b/scripts/tools/reports.lua
new file mode 100644
index 000000000..127d9fc46
--- /dev/null
+++ b/scripts/tools/reports.lua
@@ -0,0 +1,4 @@
+read_xml()
+eressea.read_game('0.dat')
+init_reports()
+write_reports()
diff --git a/scripts/tools/run-turn.lua b/scripts/tools/run-turn.lua
new file mode 100644
index 000000000..615d9eeab
--- /dev/null
+++ b/scripts/tools/run-turn.lua
@@ -0,0 +1,2 @@
+require "setup"
+run_turn()
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0aacc8d76..fde5e11f0 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -152,7 +152,15 @@ target_link_libraries(test_eressea
)
add_test(server test_eressea)
-add_test(NAME E3 WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/game-e3 COMMAND $ -e run_tests)
-add_test(NAME E2 WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/game-e2 COMMAND $ -e run_tests)
+add_test(
+ NAME E3
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/game-e3
+ COMMAND $ runtests.lua
+ )
+add_test(
+ NAME E2
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/game-e2
+ COMMAND $ runtests.lua
+ )
install(TARGETS eressea DESTINATION bin)
diff --git a/src/bind_faction.c b/src/bind_faction.c
index 0dfcb360e..0c7a7dfed 100644
--- a/src/bind_faction.c
+++ b/src/bind_faction.c
@@ -375,7 +375,14 @@ static int tolua_faction_set_locale(lua_State * L)
{
faction *self = (faction *) tolua_tousertype(L, 1, 0);
const char *name = tolua_tostring(L, 2, 0);
- self->locale = find_locale(name);
+ const struct locale *loc = find_locale(name);
+ if (loc) {
+ self->locale = loc;
+ }
+ else {
+ tolua_pushstring(L, "invalid locale");
+ return 1;
+ }
return 0;
}
diff --git a/src/bind_sqlite.c b/src/bind_sqlite.c
index 6f6c5ea29..b164068aa 100644
--- a/src/bind_sqlite.c
+++ b/src/bind_sqlite.c
@@ -15,16 +15,17 @@ without prior permission by the authors of Eressea.
#include "bind_unit.h"
#include "bindings.h"
+#include
#include
#include
#define LTYPE_DB TOLUA_CAST "db"
-extern int db_update_factions(sqlite3 * db, bool force);
+extern int db_update_factions(sqlite3 * db, bool force, int game);
static int tolua_db_update_factions(lua_State * L)
{
sqlite3 *db = (sqlite3 *) tolua_tousertype(L, 1, 0);
- db_update_factions(db, tolua_toboolean(L, 2, 0));
+ db_update_factions(db, tolua_toboolean(L, 2, 0), global.game_id);
return 0;
}
diff --git a/src/bindings.c b/src/bindings.c
index 7a8e99e3a..3d8a83388 100755
--- a/src/bindings.c
+++ b/src/bindings.c
@@ -462,9 +462,14 @@ static int tolua_init_reports(lua_State * L)
static int tolua_write_report(lua_State * L)
{
faction *f = (faction *) tolua_tousertype(L, 1, 0);
- time_t ltime = time(0);
- int result = write_reports(f, ltime);
- tolua_pushnumber(L, (lua_Number) result);
+ if (f) {
+ time_t ltime = time(0);
+ int result = write_reports(f, ltime);
+ tolua_pushnumber(L, (lua_Number)result);
+ }
+ else {
+ tolua_pushstring(L, "function expects a faction, got nil");
+ }
return 1;
}
@@ -995,10 +1000,10 @@ static int tolua_get_spells(lua_State * L)
int tolua_read_xml(lua_State * L)
{
- const char *filename = tolua_tostring(L, 1, 0);
- const char *catalog = tolua_tostring(L, 2, 0);
- init_data(filename, catalog);
- return 0;
+ const char *filename = tolua_tostring(L, 1, "config.xml");
+ const char *catalog = tolua_tostring(L, 2, "catalog.xml");
+ lua_pushinteger(L, init_data(filename, catalog));
+ return 1;
}
typedef struct event_args {
@@ -1196,7 +1201,7 @@ lua_State *lua_init(void) {
return L;
}
-int eressea_run(lua_State *L, const char *luafile, const char *entry_point)
+int eressea_run(lua_State *L, const char *luafile)
{
int err = 0;
@@ -1204,30 +1209,22 @@ int eressea_run(lua_State *L, const char *luafile, const char *entry_point)
/* run the main script */
if (luafile) {
log_debug("executing script %s\n", luafile);
+
+ lua_getglobal(L, "debug");
+ lua_getfield(L, -1, "traceback");
+ lua_remove(L, -2);
lua_getglobal(L, "dofile");
lua_pushstring(L, luafile);
- err = lua_pcall(L, 1, 0, 0);
+ err = lua_pcall(L, 1, 1, -3);
if (err != 0) {
log_lua_error(L);
- abort();
- return err;
- }
- }
- if (entry_point) {
- if (strcmp("console", entry_point)==0) {
- return lua_console(L);
- }
- lua_getglobal(L, entry_point);
- if (lua_isfunction(L, -1)) {
- log_debug("calling entry-point: %s\n", entry_point);
- err = lua_pcall(L, 0, 1, 0);
- if (err != 0) {
- log_lua_error(L);
+ } else {
+ if (lua_isnumber(L, -1)) {
+ err = (int)lua_tonumber(L, -1);
}
- return err;
- } else {
- log_error("unknown entry-point: %s\n", entry_point);
+ lua_pop(L, 1);
}
+ return err;
}
- return 0;
+ return lua_console(L);
}
diff --git a/src/bindings.h b/src/bindings.h
index 1494a1d7c..d1b880609 100755
--- a/src/bindings.h
+++ b/src/bindings.h
@@ -29,7 +29,7 @@ extern "C" {
void lua_done(struct lua_State *L);
struct lua_State *lua_init(void);
- int eressea_run(struct lua_State *L, const char *luafile, const char *entry_point);
+ int eressea_run(struct lua_State *L, const char *luafile);
#ifdef __cplusplus
}
diff --git a/src/kernel/config.c b/src/kernel/config.c
index e1647d7e4..6822d42d0 100644
--- a/src/kernel/config.c
+++ b/src/kernel/config.c
@@ -93,12 +93,13 @@ struct settings global = {
"Eressea", /* gamename */
};
+bool lomem = false;
FILE *logfile;
FILE *updatelog;
const struct race *new_race[MAXRACES];
bool sqlpatch = false;
bool battledebug = false;
-int turn = 0;
+int turn = -1;
int NewbieImmunity(void)
{
@@ -1196,19 +1197,40 @@ void update_lighthouse(building * lh)
int count_all(const faction * f)
{
#ifndef NDEBUG
- int n = 0;
- unit *u;
- for (u = f->units; u; u = u->nextF) {
- if (playerrace(u_race(u))) {
- n += u->number;
- assert(f == u->faction);
+ unit *u;
+ int np = 0, n = 0;
+ for (u = f->units; u; u = u->nextF) {
+ assert(f == u->faction);
+ n += u->number;
+ if (playerrace(u_race(u))) {
+ np += u->number;
+ }
+ }
+ if (f->num_people != np) {
+ log_error("# of people in %s is != num_people: %d should be %d.\n", factionid(f), f->num_people, np);
+ }
+ if (f->num_total != n) {
+ log_error("# of men in %s is != num_total: %d should be %d.\n", factionid(f), f->num_total, n);
}
- }
- if (f->num_people != n) {
- log_error("# of people in %s is != num_people: %d should be %d.\n", factionid(f), f->num_people, n);
- }
#endif
- return f->num_people;
+ return (f->flags & FFL_NPC) ? f->num_total : f->num_people;
+}
+
+int count_units(const faction * f)
+{
+#ifndef NDEBUG
+ unit *u;
+ int n = 0, np = 0;
+ for (u = f->units; u; u = u->nextF) {
+ ++n;
+ if (playerrace(u_race(u))) ++np;
+ }
+ n = (f->flags & FFL_NPC) ? n : np;
+ if (f->no_units && n != f->no_units) {
+ log_warning("# of units in %s is != no_units: %d should be %d.\n", factionid(f), f->no_units, n);
+ }
+#endif
+ return n;
}
int count_migrants(const faction * f)
@@ -1557,10 +1579,6 @@ void freestrlist(strlist * s)
}
}
-/* - Meldungen und Fehler ------------------------------------------------- */
-
-bool lomem = false;
-
/* - Namen der Strukturen -------------------------------------- */
typedef char name[OBJECTIDSIZE + 1];
static name idbuf[8];
@@ -3147,7 +3165,6 @@ void load_inifile(dictionary * d)
make_locales(str);
/* excerpt from [config] (the rest is used in bindings.c) */
- game_name = iniparser_getstring(d, "config:game", game_name);
-
+ global.game_id = iniparser_getint(d, "config:game_id", 0);
global.inifile = d;
}
diff --git a/src/kernel/config.h b/src/kernel/config.h
index f4b2ceff6..885684d99 100644
--- a/src/kernel/config.h
+++ b/src/kernel/config.h
@@ -256,9 +256,10 @@ extern "C" {
extern int rule_alliance_limit(void);
extern int rule_faction_limit(void);
- extern int count_all(const struct faction *f);
- extern int count_migrants(const struct faction *f);
- extern int count_maxmigrants(const struct faction *f);
+ int count_units(const struct faction * f);
+ int count_all(const struct faction *f);
+ int count_migrants(const struct faction *f);
+ int count_maxmigrants(const struct faction *f);
extern bool has_limited_skills(const struct unit *u);
extern const struct race *findrace(const char *, const struct locale *);
@@ -392,6 +393,7 @@ extern "C" {
void *vm_state;
float producexpchance;
int cookie;
+ int game_id;
int data_version; /* TODO: eliminate in favor of gamedata.version */
struct _dictionary_ *inifile;
@@ -449,7 +451,7 @@ extern "C" {
extern struct attrib_type at_guard;
extern void free_gamedata(void);
#if 1 /* disable to count all units */
-# define count_unit(u) playerrace(u_race(u))
+# define count_unit(u) (u->number>0 && playerrace(u_race(u)))
#else
# define count_unit(u) 1
#endif
diff --git a/src/kernel/faction.c b/src/kernel/faction.c
index 456cc3133..34d7071e2 100755
--- a/src/kernel/faction.c
+++ b/src/kernel/faction.c
@@ -112,7 +112,7 @@ faction *get_monsters(void)
if (!monsters) {
faction *f;
for (f = factions; f; f = f->next) {
- if (f->flags & FFL_NPC) {
+ if ((f->flags & FFL_NPC) && !(f->flags & FFL_DEFENDER)) {
return monsters = f;
}
}
diff --git a/src/kernel/sqlite.c b/src/kernel/sqlite.c
index cb9e8b903..a84cea6b2 100644
--- a/src/kernel/sqlite.c
+++ b/src/kernel/sqlite.c
@@ -129,117 +129,133 @@ db_update_email(sqlite3 * db, const faction * f, const db_faction * dbstate,
return SQLITE_OK;
}
-
-int db_update_factions(sqlite3 * db, bool force)
-{
- int game_id = 6;
- const char sql_select[] =
- "SELECT faction.id, faction.email_id, faction.code, email.email, faction.password_md5, faction.name, faction.lastturn FROM email, faction"
- " WHERE email.id=faction.email_id AND faction.game_id=? AND (lastturn IS NULL OR lastturn>?)";
- sqlite3_stmt *stmt_select = stmt_cache_get(db, sql_select);
- faction *f;
- int res;
-
- res = sqlite3_bind_int(stmt_select, 1, game_id);
- SQL_EXPECT(res, SQLITE_OK);
- res = sqlite3_bind_int(stmt_select, 2, turn - 2);
- SQL_EXPECT(res, SQLITE_OK);
- for (;;) {
- sqlite3_uint64 id_faction;
- int lastturn;
-
+/*
+int db_get_game(sqlite3 *db) {
+ int game_id = 0;
+ const char sql_game[] =
+ "SELECT id FROM game WHERE name=?";
+ sqlite3_stmt *stmt_game = stmt_cache(db, sql_game);
+ res = sqlite3_bind_text(stmt_game, 1, gamename, -1, SQLITE_TRANSIENT);
+ SQL_EXPECT(res, SQLITE_OK);
res = sqlite3_step(stmt_select);
- if (res != SQLITE_ROW)
- break;
-
- id_faction = sqlite3_column_int64(stmt_select, 0);
- lastturn = sqlite3_column_int(stmt_select, 6);
- f = get_faction_by_id((int)id_faction);
-
- if (f == NULL || !f->alive) {
- if (lastturn == 0) {
- const char sql_update[] = "UPDATE faction SET lastturn=? WHERE id=?";
- sqlite3_stmt *stmt = stmt_cache_get(db, sql_update);
-
- lastturn = f ? f->lastorders : turn - 1;
- sqlite3_bind_int(stmt, 1, lastturn);
- sqlite3_bind_int64(stmt, 2, id_faction);
- res = sqlite3_step(stmt);
- SQL_EXPECT(res, SQLITE_DONE);
- }
- } else {
- md5_state_t ms;
- md5_byte_t digest[16];
- int i;
- char passwd_md5[MD5_LENGTH_0];
- sqlite3_uint64 id_email;
- bool update = force;
- db_faction dbstate;
- const char *no_b36;
-
- fset(f, FFL_MARK);
- dbstate.id_faction = id_faction;
- dbstate.id_email = sqlite3_column_int64(stmt_select, 1);
- no_b36 = (const char *)sqlite3_column_text(stmt_select, 2);
- dbstate.no = no_b36 ? atoi36(no_b36) : -1;
- dbstate.email = (const char *)sqlite3_column_text(stmt_select, 3);
- dbstate.passwd_md5 = (const char *)sqlite3_column_text(stmt_select, 4);
- dbstate.name = (const char *)sqlite3_column_text(stmt_select, 5);
-
- id_email = dbstate.id_email;
- res = db_update_email(db, f, &dbstate, force, &id_email);
- SQL_EXPECT(res, SQLITE_OK);
-
- md5_init(&ms);
- md5_append(&ms, (md5_byte_t *) f->passw, (int)strlen(f->passw));
- md5_finish(&ms, digest);
- for (i = 0; i != 16; ++i)
- sprintf(passwd_md5 + 2 * i, "%.02x", digest[i]);
-
- if (!update) {
- update = ((id_email != 0 && dbstate.id_email != id_email)
- || dbstate.no != f->no || dbstate.passwd_md5 == NULL
- || strcmp(passwd_md5, dbstate.passwd_md5) != 0 || dbstate.name == NULL
- || strncmp(f->name, dbstate.name, MAX_FACTION_NAME) != 0);
- }
- if (update) {
- const char sql_update_faction[] =
- "UPDATE faction SET email_id=?, password_md5=?, code=?, name=?, firstturn=? WHERE id=?";
- sqlite3_stmt *stmt_update_faction =
- stmt_cache_get(db, sql_update_faction);
-
- res = sqlite3_bind_int64(stmt_update_faction, 1, id_email);
- SQL_EXPECT(res, SQLITE_OK);
- res =
- sqlite3_bind_text(stmt_update_faction, 2, passwd_md5, MD5_LENGTH,
- SQLITE_TRANSIENT);
- SQL_EXPECT(res, SQLITE_OK);
- res =
- sqlite3_bind_text(stmt_update_faction, 3, no_b36, -1,
- SQLITE_TRANSIENT);
- SQL_EXPECT(res, SQLITE_OK);
- res =
- sqlite3_bind_text(stmt_update_faction, 4, f->name, -1,
- SQLITE_TRANSIENT);
- SQL_EXPECT(res, SQLITE_OK);
- res = sqlite3_bind_int(stmt_update_faction, 5, turn - f->age);
- SQL_EXPECT(res, SQLITE_OK);
- res = sqlite3_bind_int64(stmt_update_faction, 6, f->subscription);
- SQL_EXPECT(res, SQLITE_OK);
- res = sqlite3_step(stmt_update_faction);
- SQL_EXPECT(res, SQLITE_DONE);
- }
+ return game_id;
+}
+*/
+int db_update_factions(sqlite3 * db, bool force, int game_id)
+{
+ const char sql_select[] =
+ "SELECT faction.id, faction.email_id, faction.code, email.email, faction.password_md5, faction.name, faction.lastturn FROM email, faction"
+ " WHERE email.id=faction.email_id AND faction.game_id=? AND (lastturn IS NULL OR lastturn>?)";
+ sqlite3_stmt *stmt_select = stmt_cache_get(db, sql_select);
+ faction *f;
+ int res;
+
+ res = sqlite3_bind_int(stmt_select, 1, game_id);
+ SQL_EXPECT(res, SQLITE_OK);
+ res = sqlite3_bind_int(stmt_select, 2, turn - 2);
+ SQL_EXPECT(res, SQLITE_OK);
+ for (;;) {
+ sqlite3_uint64 id_faction;
+ int lastturn;
+ const char * no_b36;
+
+ res = sqlite3_step(stmt_select);
+ if (res != SQLITE_ROW)
+ break;
+
+ id_faction = sqlite3_column_int64(stmt_select, 0);
+ lastturn = sqlite3_column_int(stmt_select, 6);
+ no_b36 = (const char *)sqlite3_column_text(stmt_select, 2);
+ f = get_faction_by_id((int)id_faction);
+ if (!f) {
+ int no = atoi36(no_b36);
+ f = findfaction(no);
+ if (f) {
+ f->subscription = (int)id_faction;
+ }
+ }
+ if (!f || !f->alive) {
+ if (lastturn == 0) {
+ const char sql_update[] = "UPDATE faction SET lastturn=? WHERE id=?";
+ sqlite3_stmt *stmt = stmt_cache_get(db, sql_update);
+
+ lastturn = f ? f->lastorders : turn - 1;
+ sqlite3_bind_int(stmt, 1, lastturn);
+ sqlite3_bind_int64(stmt, 2, id_faction);
+ res = sqlite3_step(stmt);
+ SQL_EXPECT(res, SQLITE_DONE);
+ }
+ } else {
+ md5_state_t ms;
+ md5_byte_t digest[16];
+ int i;
+ char passwd_md5[MD5_LENGTH_0];
+ sqlite3_uint64 id_email;
+ bool update = force;
+ db_faction dbstate;
+
+ fset(f, FFL_MARK);
+ dbstate.id_faction = id_faction;
+ dbstate.id_email = sqlite3_column_int64(stmt_select, 1);
+ dbstate.no = no_b36 ? atoi36(no_b36) : -1;
+ dbstate.email = (const char *)sqlite3_column_text(stmt_select, 3);
+ dbstate.passwd_md5 = (const char *)sqlite3_column_text(stmt_select, 4);
+ dbstate.name = (const char *)sqlite3_column_text(stmt_select, 5);
+
+ id_email = dbstate.id_email;
+ res = db_update_email(db, f, &dbstate, force, &id_email);
+ SQL_EXPECT(res, SQLITE_OK);
+
+ md5_init(&ms);
+ md5_append(&ms, (md5_byte_t *) f->passw, (int)strlen(f->passw));
+ md5_finish(&ms, digest);
+ for (i = 0; i != 16; ++i)
+ sprintf(passwd_md5 + 2 * i, "%.02x", digest[i]);
+
+ if (!update) {
+ update = ((id_email != 0 && dbstate.id_email != id_email)
+ || dbstate.no != f->no || dbstate.passwd_md5 == NULL
+ || strcmp(passwd_md5, dbstate.passwd_md5) != 0 || dbstate.name == NULL
+ || strncmp(f->name, dbstate.name, MAX_FACTION_NAME) != 0);
+ }
+ if (update) {
+ const char sql_update_faction[] =
+ "UPDATE faction SET email_id=?, password_md5=?, code=?, name=?, firstturn=? WHERE id=?";
+ sqlite3_stmt *stmt_update_faction =
+ stmt_cache_get(db, sql_update_faction);
+
+ res = sqlite3_bind_int64(stmt_update_faction, 1, id_email);
+ SQL_EXPECT(res, SQLITE_OK);
+ res =
+ sqlite3_bind_text(stmt_update_faction, 2, passwd_md5, MD5_LENGTH,
+ SQLITE_TRANSIENT);
+ SQL_EXPECT(res, SQLITE_OK);
+ res =
+ sqlite3_bind_text(stmt_update_faction, 3, no_b36, -1,
+ SQLITE_TRANSIENT);
+ SQL_EXPECT(res, SQLITE_OK);
+ res =
+ sqlite3_bind_text(stmt_update_faction, 4, f->name, -1,
+ SQLITE_TRANSIENT);
+ SQL_EXPECT(res, SQLITE_OK);
+ res = sqlite3_bind_int(stmt_update_faction, 5, turn - f->age);
+ SQL_EXPECT(res, SQLITE_OK);
+ res = sqlite3_bind_int64(stmt_update_faction, 6, f->subscription);
+ SQL_EXPECT(res, SQLITE_OK);
+ res = sqlite3_step(stmt_update_faction);
+ SQL_EXPECT(res, SQLITE_DONE);
+ }
+ }
}
- }
-
- for (f = factions; f; f = f->next) {
- if (!fval(f, FFL_MARK)) {
- log_error("%s (sub=%d, email=%s) has no entry in the database\n", factionname(f), f->subscription, f->email);
- } else {
- freset(f, FFL_MARK);
+
+ for (f = factions; f; f = f->next) {
+ if (!fval(f, FFL_MARK)) {
+ log_error("%s (sub=%d, email=%s) has no entry in the database\n", factionname(f), f->subscription, f->email);
+ } else {
+ freset(f, FFL_MARK);
+ }
}
- }
- return SQLITE_OK;
+ return SQLITE_OK;
}
int db_update_scores(sqlite3 * db, bool force)
diff --git a/src/kernel/unit.c b/src/kernel/unit.c
index 1be61c138..568d8161f 100644
--- a/src/kernel/unit.c
+++ b/src/kernel/unit.c
@@ -1070,43 +1070,51 @@ struct building *inside_building(const struct unit *u)
void u_setfaction(unit * u, faction * f)
{
- int cnt = u->number;
+ int cnt = u->number;
+ if (u->faction == f)
+ return;
+ if (u->faction) {
+ if (count_unit(u)) {
+ --u->faction->no_units;
+ }
+ set_number(u, 0);
+ join_group(u, NULL);
+ free_orders(&u->orders);
+ set_order(&u->thisorder, NULL);
- if (u->faction == f)
- return;
- if (u->faction) {
- set_number(u, 0);
- if (count_unit(u))
- --u->faction->no_units;
- join_group(u, NULL);
- free_orders(&u->orders);
- set_order(&u->thisorder, NULL);
+ if (u->nextF) {
+ u->nextF->prevF = u->prevF;
+ }
+ if (u->prevF) {
+ u->prevF->nextF = u->nextF;
+ }
+ else {
+ u->faction->units = u->nextF;
+ }
+ }
- if (u->nextF)
- u->nextF->prevF = u->prevF;
- if (u->prevF)
- u->prevF->nextF = u->nextF;
- else
- u->faction->units = u->nextF;
- }
+ if (f != NULL) {
+ if (f->units) {
+ f->units->prevF = u;
+ }
+ u->prevF = NULL;
+ u->nextF = f->units;
+ f->units = u;
+ }
+ else {
+ u->nextF = NULL;
+ }
- if (f != NULL) {
- if (f->units)
- f->units->prevF = u;
- u->prevF = NULL;
- u->nextF = f->units;
- f->units = u;
- } else
- u->nextF = NULL;
-
- u->faction = f;
- if (u->region)
- update_interval(f, u->region);
- if (cnt && f) {
- set_number(u, cnt);
- if (count_unit(u))
- ++f->no_units;
- }
+ u->faction = f;
+ if (u->region) {
+ update_interval(f, u->region);
+ }
+ if (cnt) {
+ set_number(u, cnt);
+ }
+ if (f && count_unit(u)) {
+ ++f->no_units;
+ }
}
/* vorsicht Sprüche können u->number == RS_FARVISION haben! */
diff --git a/src/laws.c b/src/laws.c
index 32dbb1bef..040d212b0 100755
--- a/src/laws.c
+++ b/src/laws.c
@@ -4517,10 +4517,6 @@ void init_processor(void)
add_proc_region(p, &enter_1, "Betreten (1. Versuch)");
add_proc_order(p, K_USE, &use_cmd, 0, "Benutzen");
- if (!global.disabled[K_GM]) {
- add_proc_global(p, &gmcommands, "GM Kommandos");
- }
-
p += 10; /* in case it has any effects on alliance victories */
add_proc_order(p, K_GIVE, &give_control_cmd, 0, "GIB KOMMANDO");
diff --git a/src/main.c b/src/main.c
index 34f5aa77f..5862cad43 100644
--- a/src/main.c
+++ b/src/main.c
@@ -40,7 +40,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
static const char *logfile= "eressea.log";
static const char *luafile = 0;
-static const char *entry_point = NULL;
static const char *inifile = "eressea.ini";
static int memdebug = 0;
@@ -52,8 +51,6 @@ static void parse_config(const char *filename)
log_debug("reading from configuration file %s\n", filename);
memdebug = iniparser_getint(d, "eressea:memcheck", memdebug);
- entry_point = iniparser_getstring(d, "eressea:run", entry_point);
- luafile = iniparser_getstring(d, "eressea:load", luafile);
/* only one value in the [editor] section */
force_color = iniparser_getint(d, "editor:color", force_color);
@@ -100,7 +97,7 @@ static int parse_args(int argc, char **argv, int *exitcode)
for (i = 1; i != argc; ++i) {
if (argv[i][0] != '-') {
- return usage(argv[0], argv[i]);
+ luafile = argv[i];
} else if (argv[i][1] == '-') { /* long format */
if (strcmp(argv[i] + 2, "version") == 0) {
printf("\n%s PBEM host\n"
@@ -118,18 +115,12 @@ static int parse_args(int argc, char **argv, int *exitcode)
} else {
const char *arg;
switch (argv[i][1]) {
- case 'C':
- entry_point = "console";
- break;
case 'f':
i = get_arg(argc, argv, 2, i, &luafile, 0);
break;
case 'l':
i = get_arg(argc, argv, 2, i, &logfile, 0);
break;
- case 'e':
- i = get_arg(argc, argv, 2, i, &entry_point, 0);
- break;
case 't':
i = get_arg(argc, argv, 2, i, &arg, 0);
turn = atoi(arg);
@@ -138,7 +129,6 @@ static int parse_args(int argc, char **argv, int *exitcode)
verbosity = 0;
break;
case 'r':
- entry_point = "run_turn";
i = get_arg(argc, argv, 2, i, &arg, 0);
turn = atoi(arg);
break;
@@ -271,7 +261,7 @@ int main(int argc, char **argv)
register_spells();
bind_monsters(L);
- err = eressea_run(L, luafile, entry_point);
+ err = eressea_run(L, luafile);
if (err) {
log_error("server execution failed with code %d\n", err);
return err;
diff --git a/src/modules/gmcmd.c b/src/modules/gmcmd.c
index 581ad27ee..12845df9d 100644
--- a/src/modules/gmcmd.c
+++ b/src/modules/gmcmd.c
@@ -54,65 +54,25 @@
#include
#include
-/**
- ** at_permissions
- **/
-
-static void mistake(const unit * u, struct order *ord, const char *comment)
-{
- if (!is_monsters(u->faction)) {
- ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "mistake",
- "error", comment));
- }
-}
-
-static void
-write_permissions(const attrib * a, const void *owner, struct storage *store)
-{
- a_write(store, (attrib *) a->data.v, owner);
-}
-
static int read_permissions(attrib * a, void *owner, struct storage *store)
{
attrib *attr = NULL;
a_read(store, &attr, NULL);
- a->data.v = attr;
+ a_free(attr);
return AT_READ_OK;
}
struct attrib_type at_permissions = {
"GM:permissions",
NULL, NULL, NULL,
- write_permissions, read_permissions,
+ NULL, read_permissions,
ATF_UNIQUE
};
-attrib *make_atpermissions(void)
-{
- return a_new(&at_permissions);
-}
-
-/**
- ** GM: CREATE
- **/
-
-static void
-write_gmcreate(const attrib * a, const void *owner, struct storage *store)
-{
- const item_type *itype = (const item_type *)a->data.v;
- assert(itype);
- WRITE_TOK(store, resourcename(itype->rtype, 0));
-}
-
static int read_gmcreate(attrib * a, void *owner, struct storage *store)
{
char zText[32];
READ_TOK(store, zText, sizeof(zText));
- a->data.v = it_find(zText);
- if (a->data.v == NULL) {
- log_error("unknown itemtype %s in gmcreate attribute\n", zText);
- return AT_READ_FAIL;
- }
return AT_READ_OK;
}
@@ -120,640 +80,11 @@ static int read_gmcreate(attrib * a, void *owner, struct storage *store)
attrib_type at_gmcreate = {
"GM:create",
NULL, NULL, NULL,
- write_gmcreate, read_gmcreate
+ NULL, read_gmcreate
};
-attrib *make_atgmcreate(const struct item_type * itype)
-{
- attrib *a = a_new(&at_gmcreate);
- a->data.v = (void *)itype;
- return a;
-}
-
-static void gm_create(const void *tnext, struct unit *u, struct order *ord)
-{
- int i;
- attrib *permissions = a_find(u->faction->attribs, &at_permissions);
- if (permissions)
- permissions = (attrib *) permissions->data.v;
- if (!permissions)
- return;
- i = getint();
-
- if (i > 0) {
- const char *iname = getstrtoken();
- const item_type *itype = finditemtype(iname, u->faction->locale);
- if (itype == NULL) {
- mistake(u, ord, "unknown item.");
- } else {
- attrib *a = a_find(permissions, &at_gmcreate);
-
- while (a && a->type == &at_gmcreate && a->data.v != (void *)itype)
- a = a->next;
- if (a)
- i_change(&u->items, itype, i);
- else
- mistake(u, ord, "your faction cannot create this item.");
- }
- }
-}
-
-static bool has_permission(const attrib * permissions, unsigned int key)
-{
- return (find_key((attrib *) permissions->data.v, key) ||
- find_key((attrib *) permissions->data.v, atoi36("master")));
-}
-
-/**
- ** GM: GATE
- ** requires: permission-key "gmgate"
- **/
-static void gm_gate(const void *tnext, struct unit * u, struct order *ord)
-{
- const struct plane *pl = rplane(u->region);
- int id = getid();
- int x = rel_to_abs(pl, u->faction, getint(), 0);
- int y = rel_to_abs(pl, u->faction, getint(), 1);
- building *b = findbuilding(id);
- region *r;
-
- pnormalize(&x, &y, pl);
- r = findregion(x, y);
- if (b == NULL || r == NULL || pl != rplane(b->region) || pl != rplane(r)) {
- mistake(u, ord, "the unit cannot transform this building.");
- return;
- } else {
- /* checking permissions */
- attrib *permissions = a_find(u->faction->attribs, &at_permissions);
- if (permissions && has_permission(permissions, atoi36("gmgate"))) {
- remove_triggers(&b->attribs, "timer", &tt_gate);
- remove_triggers(&b->attribs, "create", &tt_unguard);
- if (r != b->region) {
- add_trigger(&b->attribs, "timer", trigger_gate(b, r));
- add_trigger(&b->attribs, "create", trigger_unguard(b));
- fset(b, BLD_UNGUARDED);
- }
- }
- }
-}
-
-/**
- ** GM: TERRAFORM
- ** requires: permission-key "gmterf"
- **/
-static void gm_terraform(const void *tnext, struct unit *u, struct order *ord)
-{
- const struct plane *p = rplane(u->region);
- int x = rel_to_abs(p, u->faction, getint(), 0);
- int y = rel_to_abs(p, u->faction, getint(), 1);
- const char *c = getstrtoken();
- variant token;
- void **tokens = get_translations(u->faction->locale, UT_TERRAINS);
- region *r;
- pnormalize(&x, &y, p);
- r = findregion(x, y);
-
- if (r == NULL || p != rplane(r)) {
- mistake(u, ord, "region is in another plane.");
- return;
- } else {
- /* checking permissions */
- attrib *permissions = a_find(u->faction->attribs, &at_permissions);
- if (!permissions || !has_permission(permissions, atoi36("gmterf")))
- return;
- }
-
- if (findtoken(*tokens, c, &token) != E_TOK_NOMATCH) {
- const terrain_type *terrain = (const terrain_type *)token.v;
- terraform_region(r, terrain);
- }
-}
-
-/**
- ** GM: TELEPORT
- ** requires: permission-key "gmtele"
- **/
-static void gm_teleport(const void *tnext, struct unit *u, struct order *ord)
-{
- const struct plane *p = rplane(u->region);
- unit *to = findunit(getid());
- int x = rel_to_abs(p, u->faction, getint(), 0);
- int y = rel_to_abs(p, u->faction, getint(), 1);
- region *r = findregion(x, y);
-
- if (r == NULL || p != rplane(r)) {
- mistake(u, ord, "region is in another plane.");
- } else if (to == NULL) {
- ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found",
- ""));
- } else if (rplane(to->region) != rplane(r) && !ucontact(to, u)) {
- ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_no_contact",
- "target", to));
- } else {
- /* checking permissions */
- attrib *permissions = a_find(u->faction->attribs, &at_permissions);
- if (!permissions || !has_permission(permissions, atoi36("gmtele"))) {
- mistake(u, ord, "permission denied.");
- } else
- move_unit(to, r, NULL);
- }
-}
-
-/**
- ** GM: TELL PLANE
- ** requires: permission-key "gmmsgr"
- **/
-static void gm_messageplane(const void *tnext, struct unit *gm, struct order *ord)
-{
- const struct plane *p = rplane(gm->region);
- const char *zmsg = getstrtoken();
- if (p == NULL) {
- mistake(gm, ord, "In diese Ebene kann keine Nachricht gesandt werden.");
- } else {
- /* checking permissions */
- attrib *permissions = a_find(gm->faction->attribs, &at_permissions);
- if (!permissions || !has_permission(permissions, atoi36("gmmsgr"))) {
- mistake(gm, ord, "permission denied.");
- } else {
- message *msg = msg_message("msg_event", "string", zmsg);
- faction *f;
- region *r;
- for (f = factions; f; f = f->next) {
- freset(f, FFL_SELECT);
- }
- for (r = regions; r; r = r->next) {
- unit *u;
- if (rplane(r) != p)
- continue;
- for (u = r->units; u; u = u->next)
- if (!fval(u->faction, FFL_SELECT)) {
- f = u->faction;
- fset(f, FFL_SELECT);
- add_message(&f->msgs, msg);
- }
- }
- msg_release(msg);
- }
- }
-}
-
-static void
-gm_messagefaction(const void *tnext, struct unit *gm, struct order *ord)
-{
- int n = getid();
- faction *f = findfaction(n);
- const char *msg = getstrtoken();
- plane *p = rplane(gm->region);
- attrib *permissions = a_find(gm->faction->attribs, &at_permissions);
- if (!permissions || !has_permission(permissions, atoi36("gmmsgr"))) {
- mistake(gm, ord, "permission denied.");
- return;
- }
- if (f != NULL) {
- region *r;
- for (r = regions; r; r = r->next)
- if (rplane(r) == p) {
- unit *u;
- for (u = r->units; u; u = u->next)
- if (u->faction == f) {
- add_message(&f->msgs, msg_message("msg_event", "string", msg));
- return;
- }
- }
- }
- mistake(gm, ord, "cannot send messages to this faction.");
-}
-
-/**
- ** GM: TELL REGION
- ** requires: permission-key "gmmsgr"
- **/
-static void gm_messageregion(const void *tnext, struct unit *u, struct order *ord)
-{
- const struct plane *p = rplane(u->region);
- int x = rel_to_abs(p, u->faction, getint(), 0);
- int y = rel_to_abs(p, u->faction, getint(), 1);
- const char *msg = getstrtoken();
- region *r = findregion(x, y);
-
- if (r == NULL || p != rplane(r)) {
- mistake(u, ord, "region is in another plane.");
- } else {
- /* checking permissions */
- attrib *permissions = a_find(u->faction->attribs, &at_permissions);
- if (!permissions || !has_permission(permissions, atoi36("gmmsgr"))) {
- mistake(u, ord, "permission denied.");
- } else {
- add_message(&r->msgs, msg_message("msg_event", "string", msg));
- }
- }
-}
-
-/**
- ** GM: KILL UNIT
- ** requires: permission-key "gmkill"
- **/
-static void gm_killunit(const void *tnext, struct unit *u, struct order *ord)
-{
- const struct plane *p = rplane(u->region);
- unit *target = findunit(getid());
- const char *msg = getstrtoken();
- region *r = target->region;
-
- if (r == NULL || p != rplane(r)) {
- mistake(u, ord, "region is in another plane.");
- } else {
- /* checking permissions */
- attrib *permissions = a_find(u->faction->attribs, &at_permissions);
- if (!permissions || !has_permission(permissions, atoi36("gmkill"))) {
- mistake(u, ord, "permission denied.");
- } else {
- scale_number(target, 0);
- ADDMSG(&target->faction->msgs, msg_message("killedbygm",
- "region unit string", r, target, msg));
- }
- }
-}
-
-/**
- ** GM: KILL FACTION
- ** requires: permission-key "gmmsgr"
- **/
-static void gm_killfaction(const void *tnext, struct unit *u, struct order *ord)
-{
- int n = getid();
- faction *f = findfaction(n);
- const char *msg = getstrtoken();
- plane *p = rplane(u->region);
- attrib *permissions = a_find(u->faction->attribs, &at_permissions);
- if (!permissions || !has_permission(permissions, atoi36("gmkill"))) {
- mistake(u, ord, "permission denied.");
- return;
- }
- if (f != NULL) {
- region *r;
- for (r = regions; r; r = r->next)
- if (rplane(r) == p) {
- unit *target;
- for (target = r->units; target; target = target->next) {
- if (target->faction == f) {
- scale_number(target, 0);
- ADDMSG(&target->faction->msgs, msg_message("killedbygm",
- "region unit string", r, target, msg));
- return;
- }
- }
- }
- }
- mistake(u, ord, "cannot remove a unit from this faction.");
-}
-
-/**
- ** GM: TELL
- ** requires: permission-key "gmmsgr"
- **/
-static void gm_messageunit(const void *tnext, struct unit *u, struct order *ord)
-{
- const struct plane *p = rplane(u->region);
- unit *target = findunit(getid());
- const char *msg = getstrtoken();
- region *r;
-
- if (target == NULL) {
- ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found",
- ""));
- return;
- }
-
- r = target->region;
-
- if (r == NULL || p != rplane(r)) {
- mistake(u, ord, "region is in another plane.");
- } else {
- /* checking permissions */
- attrib *permissions = a_find(u->faction->attribs, &at_permissions);
- if (!permissions || !has_permission(permissions, atoi36("gmmsgu"))) {
- mistake(u, ord, "permission denied.");
- } else {
- add_message(&target->faction->msgs,
- msg_message("regionmessage", "region sender string", r, u, msg));
- }
- }
-}
-
-/**
- ** GM: GIVE
- ** requires: permission-key "gmgive"
- **/
-static void gm_give(const void *tnext, struct unit *u, struct order *ord)
-{
- unit *to = findunit(getid());
- int num = getint();
- const item_type *itype = finditemtype(getstrtoken(), u->faction->locale);
-
- if (to == NULL || rplane(to->region) != rplane(u->region)) {
- /* unknown or in another plane */
- ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found",
- ""));
- } else if (itype == NULL || i_get(u->items, itype) == 0) {
- /* unknown or not enough */
- mistake(u, ord, "invalid item or item not found.");
- } else {
- /* checking permissions */
- attrib *permissions = a_find(u->faction->attribs, &at_permissions);
- if (!permissions || !has_permission(permissions, atoi36("gmgive"))) {
- mistake(u, ord, "permission denied.");
- } else {
- int i = i_get(u->items, itype);
- if (i < num)
- num = i;
- if (num) {
- i_change(&u->items, itype, -num);
- i_change(&to->items, itype, num);
- }
- }
- }
-}
-
-/**
- ** GM: TAKE
- ** requires: permission-key "gmtake"
- **/
-static void gm_take(const void *tnext, struct unit *u, struct order *ord)
-{
- unit *to = findunit(getid());
- int num = getint();
- const item_type *itype = finditemtype(getstrtoken(), u->faction->locale);
-
- if (to == NULL || rplane(to->region) != rplane(u->region)) {
- /* unknown or in another plane */
- ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found",
- ""));
- } else if (itype == NULL || i_get(to->items, itype) == 0) {
- /* unknown or not enough */
- mistake(u, ord, "invalid item or item not found.");
- } else {
- /* checking permissions */
- attrib *permissions = a_find(u->faction->attribs, &at_permissions);
- if (!permissions || !has_permission(permissions, atoi36("gmtake"))) {
- mistake(u, ord, "permission denied.");
- } else {
- int i = i_get(to->items, itype);
- if (i < num)
- num = i;
- if (num) {
- i_change(&to->items, itype, -num);
- i_change(&u->items, itype, num);
- }
- }
- }
-}
-
-/**
- ** GM: SKILL
- ** requires: permission-key "gmskil"
- **/
-static void gm_skill(const void *tnext, struct unit *u, struct order *ord)
-{
- unit *to = findunit(getid());
- skill_t skill = findskill(getstrtoken(), u->faction->locale);
- int num = getint();
-
- if (to == NULL || rplane(to->region) != rplane(u->region)) {
- /* unknown or in another plane */
- ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found",
- ""));
- } else if (skill == NOSKILL || skill == SK_MAGIC || skill == SK_ALCHEMY) {
- /* unknown or not enough */
- mistake(u, ord, "unknown skill, or skill cannot be raised.");
- } else if (num < 0 || num > 30) {
- /* sanity check failed */
- mistake(u, ord, "invalid value.");
- } else {
- /* checking permissions */
- attrib *permissions = a_find(u->faction->attribs, &at_permissions);
- if (!permissions || !has_permission(permissions, atoi36("gmskil"))) {
- mistake(u, ord, "permission denied.");
- } else {
- set_level(to, skill, num);
- }
- }
-}
-
-static void * g_keys;
-static void * g_root;
-static void * g_tell;
-static void * g_kill;
-
void register_gmcmd(void)
{
at_register(&at_gmcreate);
at_register(&at_permissions);
- add_command(&g_root, &g_keys, "gm", NULL);
- add_command(&g_keys, NULL, "terraform", &gm_terraform);
- add_command(&g_keys, NULL, "create", &gm_create);
- add_command(&g_keys, NULL, "gate", &gm_gate);
- add_command(&g_keys, NULL, "give", &gm_give);
- add_command(&g_keys, NULL, "take", &gm_take);
- add_command(&g_keys, NULL, "teleport", &gm_teleport);
- add_command(&g_keys, NULL, "skill", &gm_skill);
- add_command(&g_keys, &g_tell, "tell", NULL);
- add_command(&g_tell, NULL, "region", &gm_messageregion);
- add_command(&g_tell, NULL, "unit", &gm_messageunit);
- add_command(&g_tell, NULL, "plane", &gm_messageplane);
- add_command(&g_tell, NULL, "faction", &gm_messagefaction);
- add_command(&g_keys, &g_kill, "kill", NULL);
- add_command(&g_kill, NULL, "unit", &gm_killunit);
- add_command(&g_kill, NULL, "faction", &gm_killfaction);
-}
-
-/*
- * execute gm-commands for all units in the game
- */
-
-void gmcommands(void)
-{
- region **rp = ®ions;
- while (*rp) {
- region *r = *rp;
- unit **up = &r->units;
- while (*up) {
- unit *u = *up;
- struct order *ord;
- for (ord = u->orders; ord; ord = ord->next) {
- if (get_keyword(ord) == K_GM) {
- do_command(&g_root, u, ord);
- }
- }
- if (u == *up)
- up = &u->next;
- }
- if (*rp == r)
- rp = &r->next;
- }
-}
-
-#define EXTENSION 10000
-
-faction *gm_addquest(const char *email, const char *name, int radius,
- unsigned int flags)
-{
- plane *pl;
- watcher *w = calloc(sizeof(watcher), 1);
- region *center;
- bool invalid = false;
- int minx, miny, maxx, maxy, cx, cy;
- int x;
- faction *f;
-
- /* GM playfield */
- do {
- minx = ((rng_int() % (2 * EXTENSION)) - EXTENSION);
- miny = ((rng_int() % (2 * EXTENSION)) - EXTENSION);
- for (x = 0; !invalid && x <= radius * 2; ++x) {
- int y;
- for (y = 0; !invalid && y <= radius * 2; ++y) {
- region *r = findregion(minx + x, miny + y);
- if (r)
- invalid = true;
- }
- }
- } while (invalid);
- maxx = minx + 2 * radius;
- cx = minx + radius;
- maxy = miny + 2 * radius;
- cy = miny + radius;
- pl = create_new_plane(rng_int(), name, minx, maxx, miny, maxy, flags);
- center = new_region(cx, cy, pl, 0);
- for (x = 0; x <= 2 * radius; ++x) {
- int y;
- for (y = 0; y <= 2 * radius; ++y) {
- region *r = findregion(minx + x, miny + y);
- if (!r) {
- r = new_region(minx + x, miny + y, pl, 0);
- }
- freset(r, RF_ENCOUNTER);
- if (distance(r, center) == radius) {
- terraform_region(r, newterrain(T_FIREWALL));
- } else if (r == center) {
- terraform_region(r, newterrain(T_PLAIN));
- } else {
- terraform_region(r, newterrain(T_OCEAN));
- }
- }
- }
-
- /* watcher: */
- f = gm_addfaction(email, pl, center);
- w->faction = f;
- w->mode = see_unit;
- w->next = pl->watchers;
- pl->watchers = w;
-
- return f;
-}
-
-faction *gm_addfaction(const char *email, plane * p, region * r)
-{
- attrib *a;
- unit *u;
- faction *f = calloc(1, sizeof(faction));
-
- assert(p != NULL);
-
- /* GM faction */
- a_add(&f->attribs, make_key(atoi36("quest")));
- f->banner = _strdup("quest faction");
- f->name = _strdup("quest faction");
- f->passw = _strdup(itoa36(rng_int()));
- if (set_email(&f->email, email) != 0) {
- log_error("Invalid email address for faction %s: %s\n", itoa36(f->no), email);
- }
- f->race = new_race[RC_TEMPLATE];
- f->age = 0;
- f->lastorders = turn;
- f->alive = true;
- f->locale = default_locale;
- f->options =
- want(O_COMPRESS) | want(O_REPORT) | want(O_COMPUTER) | want(O_ADRESSEN);
- {
- faction *xist;
- int id = atoi36("gm00") - 1;
- do {
- xist = findfaction(++id);
- } while (xist);
-
- f->no = id;
- addlist(&factions, f);
- fhash(f);
- }
-
- /* generic permissions */
- a = a_add(&f->attribs, a_new(&at_permissions));
- if (a) {
- attrib *ap = (attrib *) a->data.v;
- const char *keys[] =
- { "gmterf", "gmtele", "gmgive", "gmskil", "gmtake", "gmmsgr", "gmmsgu",
- "gmgate", 0 };
- const char **key_p = keys;
- while (*key_p) {
- add_key(&ap, atoi36(*key_p));
- ++key_p;
- }
- a_add(&ap, make_atgmcreate(resource2item(r_silver)));
-
- a->data.v = ap;
- }
-
- /* one initial unit */
- u = create_unit(r, f, 1, new_race[RC_TEMPLATE], 1, "quest master", NULL);
- u->irace = new_race[RC_GNOME];
-
- return f;
-}
-
-plane *gm_addplane(int radius, unsigned int flags, const char *name)
-{
- region *center;
- plane *pl;
- bool invalid = false;
- int minx, miny, maxx, maxy, cx, cy;
- int x;
-
- /* GM playfield */
- do {
- minx = (rng_int() % (2 * EXTENSION)) - EXTENSION;
- miny = (rng_int() % (2 * EXTENSION)) - EXTENSION;
- for (x = 0; !invalid && x <= radius * 2; ++x) {
- int y;
- for (y = 0; !invalid && y <= radius * 2; ++y) {
- region *r = findregion(minx + x, miny + y);
- if (r)
- invalid = true;
- }
- }
- } while (invalid);
- maxx = minx + 2 * radius;
- cx = minx + radius;
- maxy = miny + 2 * radius;
- cy = miny + radius;
- pl = create_new_plane(rng_int(), name, minx, maxx, miny, maxy, flags);
- center = new_region(cx, cy, pl, 0);
- for (x = 0; x <= 2 * radius; ++x) {
- int y;
- for (y = 0; y <= 2 * radius; ++y) {
- region *r = findregion(minx + x, miny + y);
- if (!r)
- r = new_region(minx + x, miny + y, pl, 0);
- freset(r, RF_ENCOUNTER);
- if (distance(r, center) == radius) {
- terraform_region(r, newterrain(T_FIREWALL));
- } else if (r == center) {
- terraform_region(r, newterrain(T_PLAIN));
- } else {
- terraform_region(r, newterrain(T_OCEAN));
- }
- }
- }
- return pl;
}
diff --git a/src/report.c b/src/report.c
index 415897f3a..261d0a2cc 100644
--- a/src/report.c
+++ b/src/report.c
@@ -2199,8 +2199,14 @@ report_plaintext(const char *filename, report_context * ctx,
f->num_people = no_people;
}
#else
- no_units = f->no_units;
- no_people = f->num_people;
+ no_units = count_units(f);
+ no_people = count_all(f);
+ if (f->flags & FFL_NPC) {
+ no_people = f->num_total;
+ }
+ else {
+ no_people = f->num_people;
+ }
#endif
m = msg_message("nr_population", "population units", no_people, no_units);
nr_render(m, f->locale, buf, sizeof(buf), f);
diff --git a/src/util/attrib.c b/src/util/attrib.c
index 197fea808..8c8bf9d35 100644
--- a/src/util/attrib.c
+++ b/src/util/attrib.c
@@ -321,7 +321,7 @@ int a_read(struct storage *store, attrib ** attribs, void *owner)
}
}
} else {
- assert(!"fehler: keine laderoutine für attribut");
+ assert(!"error: no registered callback can read attribute");
}
READ_TOK(store, zText, sizeof(zText));
diff --git a/src/util/xml.c b/src/util/xml.c
index c97460912..0755e7331 100644
--- a/src/util/xml.c
+++ b/src/util/xml.c
@@ -108,29 +108,28 @@ int read_xml(const char *filename, const char *catalog)
{
xml_reader *reader = xmlReaders;
xmlDocPtr doc;
+ int result;
if (catalog) {
xmlLoadCatalog(catalog);
}
-#ifdef XML_PARSE_XINCLUDE
- doc = xmlReadFile(filename, NULL, XML_PARSE_XINCLUDE);
-#else
- doc = xmlParseFile(filename);
-#endif
+ doc = xmlReadFile(filename, NULL, XML_PARSE_XINCLUDE | XML_PARSE_NONET | XML_PARSE_PEDANTIC | XML_PARSE_COMPACT);
if (doc == NULL) {
log_error("could not open '%s'\n", filename);
return -1;
}
- xmlXIncludeProcess(doc);
-
- while (reader != NULL) {
- int i = reader->callback(doc);
- if (i != 0) {
- return i;
- }
- reader = reader->next;
+ result = xmlXIncludeProcessFlags(doc, XML_PARSE_XINCLUDE | XML_PARSE_NONET | XML_PARSE_PEDANTIC | XML_PARSE_COMPACT);
+ if (result >= 0) {
+ while (reader != NULL) {
+ int i = reader->callback(doc);
+ if (i != 0) {
+ return i;
+ }
+ reader = reader->next;
+ }
+ result = 0;
}
xmlFreeDoc(doc);
- return 0;
+ return result;
}