Merge branch 'master' of github.com:eressea/server

This commit is contained in:
Enno Rehling 2014-05-05 00:00:10 -07:00
commit 465f3f7ce9
50 changed files with 1134 additions and 1181 deletions

1
.gitignore vendored
View File

@ -27,3 +27,4 @@ game-e3/reports/
*.log
*.log.*
tags
Thumbs.db

View File

@ -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

View File

@ -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

28
doc/chapters.md Normal file
View File

@ -0,0 +1,28 @@
# Eressea Rules
&copy; 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

79
doc/introduction.md Normal file
View File

@ -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.

View File

@ -5,6 +5,12 @@
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
<rewriteURI
uriStartString="eressea:///core/"
uriStartString="config:///game/"
rewritePrefix="../res/eressea/" />
<rewriteURI
uriStartString="config:///default/"
rewritePrefix="../res/" />
<rewriteURI
uriStartString="config:///core/"
rewritePrefix="../core/res/" />
</catalog>

View File

@ -1,36 +1,37 @@
<?xml version="1.0"?>
<eressea xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="eressea:///core/messages.xml"/>
<xi:include href="config:///core/messages.xml"/>
<!-- Localization -->
<xi:include href="eressea:///core/de/strings.xml"/>
<xi:include href="eressea:///core/en/strings.xml"/>
<xi:include href="eressea/spellinfo.xml" />
<xi:include href="config:///core/de/strings.xml"/>
<xi:include href="config:///core/en/strings.xml"/>
<xi:include href="config:///game/spellinfo.xml" />
<xi:include href="eressea:///core/common/items.xml" />
<xi:include href="eressea:///core/common/armor.xml" />
<xi:include href="eressea:///core/common/weapons.xml" />
<xi:include href="eressea:///core/common/resources.xml" />
<xi:include href="eressea:///core/common/luxuries.xml" />
<xi:include href="eressea:///core/common/herbs.xml" />
<xi:include href="eressea:///core/common/potions.xml" />
<xi:include href="eressea:///core/spoils.xml"/>
<xi:include href="eressea/races.xml"/>
<xi:include href="eressea:///core/prefixes.xml"/>
<xi:include href="eressea:///core/ships.xml"/>
<xi:include href="eressea:///core/common/buildings.xml"/>
<xi:include href="eressea:///core/equipment.xml"/>
<xi:include href="eressea:///core/terrains.xml"/>
<xi:include href="eressea/terrains.xml"/>
<xi:include href="directions.xml"/>
<xi:include href="eressea/artrewards.xml"/>
<xi:include href="eressea/buildings.xml"/>
<xi:include href="eressea:///core/calendar.xml"/>
<xi:include href="config:///core/common/items.xml" />
<xi:include href="config:///core/common/armor.xml" />
<xi:include href="config:///core/common/weapons.xml" />
<xi:include href="config:///core/common/resources.xml" />
<xi:include href="config:///core/common/luxuries.xml" />
<xi:include href="config:///core/common/herbs.xml" />
<xi:include href="config:///core/common/potions.xml" />
<xi:include href="config:///core/spoils.xml"/>
<xi:include href="config:///game/races.xml"/>
<xi:include href="config:///core/prefixes.xml"/>
<xi:include href="config:///core/ships.xml"/>
<xi:include href="config:///core/common/buildings.xml"/>
<xi:include href="config:///core/equipment.xml"/>
<xi:include href="config:///core/terrains.xml"/>
<xi:include href="config:///game/terrains.xml"/>
<xi:include href="config:///default/directions.xml"/>
<xi:include href="config:///game/artrewards.xml"/>
<xi:include href="config:///game/buildings.xml"/>
<xi:include href="config:///core/calendar.xml"/>
<calendar name="secondage" newyear="month_1" start="184"/>
<xi:include href="eressea/equipment.xml"/>
<xi:include href="eressea/items.xml"/>
<xi:include href="eressea/spells.xml"/>
<xi:include href="eressea/strings.xml"/>
<xi:include href="config:///game/equipment.xml"/>
<xi:include href="config:///game/items.xml"/>
<xi:include href="config:///game/spells.xml"/>
<xi:include href="config:///game/strings.xml"/>
<xi:include href="config:///default/adamantium.xml"/>
<equipment>
<set name="first_unit">
<item name="conquesttoken" amount="1"/>
@ -43,11 +44,11 @@
</set>
</equipment>
<xi:include href="names-undead.xml"/>
<xi:include href="names-skeletons.xml"/>
<xi:include href="names-zombies.xml"/>
<xi:include href="names-ghouls.xml"/>
<xi:include href="names-dragons.xml"/>
<xi:include href="config:///default/names-undead.xml"/>
<xi:include href="config:///default/names-skeletons.xml"/>
<xi:include href="config:///default/names-zombies.xml"/>
<xi:include href="config:///default/names-ghouls.xml"/>
<xi:include href="config:///default/names-dragons.xml"/>
<game name="Eressea">
<!-- Game specific settings -->

View File

@ -1,6 +1,5 @@
[eressea]
base = .
load = setup.lua
report = reports
verbose = 0
lomem = 0

2
game-e2/runtests.lua Normal file
View File

@ -0,0 +1,2 @@
require "setup"
run_tests()

View File

@ -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"

View File

@ -4,13 +4,13 @@
"http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd">
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
<rewriteURI
uriStartString="config:///core/"
rewritePrefix="../core/res/" />
<rewriteURI
uriStartString="config:///game/"
rewritePrefix="../res/e3a/" />
<rewriteURI
uriStartString="config:///default/"
rewritePrefix="../res/" />
<rewriteURI
uriStartString="config:///core/"
rewritePrefix="../core/res/" />
</catalog>

2
game-e3/runtests.lua Normal file
View File

@ -0,0 +1,2 @@
require "setup"
run_tests()

View File

@ -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"

72
process/compress.py Executable file
View File

@ -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()

2
process/orders-accept Executable file
View File

@ -0,0 +1,2 @@
#/bin/.sh
grep -v '>From' | $HOME/src/scripts/bin/orders-accept $*

218
process/orders-process Executable file
View File

@ -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 <eressea-server@eressea.de>"
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)

16
process/orders.cron Executable file
View File

@ -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

35
process/send-bz2-report Executable file
View File

@ -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

46
process/send-zip-report Executable file
View File

@ -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

36
res/adamantium.xml Normal file
View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="iso-8859-1" ?>
<resources>
<resource name="adamantium" limited="yes" material="rm_adamantium">
<item weight="200" score="200">
<construction skill="mining" minskill="8" reqsize="1"/>
</item>
<resourcelimit>
<modifier type="require" building="mine"/>
<guard flag="mining"/>
</resourcelimit>
</resource>
<resource name="adamantiumaxe">
<item weight="100" score="500">
<construction skill="weaponsmithing" minskill="8" reqsize="1">
<requirement type="adamantium" quantity="1"/>
<requirement type="log" quantity="1"/>
</construction>
<weapon cut="true" skill="melee" offmod="2" defmod="-2" magres="0.30">
<damage type="rider" value="3d4+15"/>
<damage type="footman" value="3d4+15"/>
</weapon>
</item>
</resource>
<resource name="adamantiumplate">
<item weight="100" score="2000">
<construction skill="armorer" minskill="10" reqsize="1">
<requirement type="adamantium" quantity="3"/>
</construction>
<armor ac="7" penalty="0.1"/>
</item>
</resource>
</resources>

View File

@ -1,10 +0,0 @@
<?xml version="1.0"?>
<!DOCTYPE catalog
PUBLIC "-//OASIS/DTD Entity Resolution XML Catalog V1.0//EN"
"http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd">
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
<rewriteURI
uriStartString="eressea:///core/"
rewritePrefix="../core/res/" />
</catalog>

View File

@ -1,189 +0,0 @@
<?xml version="1.0"?>
<eressea xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="eressea:///core/messages.xml"/>
<!-- Localization -->
<xi:include href="eressea:///core/de/strings.xml"/>
<xi:include href="eressea:///core/en/strings.xml"/>
<xi:include href="eressea:///core/common/items.xml"/>
<xi:include href="eressea:///core/common/herbs.xml" />
<xi:include href="eressea:///core/spoils.xml"/>
<xi:include href="eressea:///core/prefixes.xml"/>
<xi:include href="eressea:///core/common/buildings.xml"/>
<xi:include href="eressea:///core/equipment.xml"/>
<xi:include href="directions.xml"/>
<xi:include href="e3a/ships.xml"/>
<xi:include href="e3a/shipnames.xml"/>
<xi:include href="e3a/terrains.xml"/>
<xi:include href="eressea:///core/calendar.xml"/>
<calendar name="thirdage" newyear="month_1" start="1"/>
<xi:include href="e3a/items.xml" />
<xi:include href="e3a/strings.xml"/>
<xi:include href="e3a/messages.xml"/>
<xi:include href="e3a/races.xml"/>
<xi:include href="e3a/buildings.xml"/>
<xi:include href="e3a/equipment.xml"/>
<xi:include href="e3a/spells.xml"/>
<xi:include href="e3a/luxuries.xml" />
<xi:include href="e3a/weapons.xml" />
<xi:include href="e3a/armor.xml" />
<xi:include href="e3a/resources.xml" />
<equipment>
<set name="first_unit">
<item name="conquesttoken" amount="1"/>
<item name="log" amount="10"/>
<item name="stone" amount="10"/>
<item name="money" amount="5000"/>
</set>
</equipment>
<xi:include href="names-undead.xml"/>
<xi:include href="names-skeletons.xml"/>
<xi:include href="names-zombies.xml"/>
<xi:include href="names-ghouls.xml"/>
<xi:include href="names-dragons.xml"/>
<game name="E3">
<!-- Game specific settings -->
<param name="database.gameid" value="7"></param>
<order name="BETEN" disable="yes"/>
<order name="BELAGERE" disable="yes"/>
<order name="BEKLAUEN" disable="yes"/>
<order name="FRIEDEN" disable="yes"/>
<order name="GM" disable="yes"/>
<order name="JIHAD" disable="yes"/>
<order name="KRIEG" disable="yes"/>
<order name="KAUFEN" disable="yes"/>
<order name="LEHREN" disable="yes"/>
<order name="LIEFERE" disable="yes"/>
<order name="OPFERE" disable="yes"/>
<order name="SABOTIEREN" disable="yes"/>
<order name="SPIONIEREN" disable="yes"/>
<order name="TREIBEN" disable="yes"/>
<order name="UNTERHALTEN" disable="yes"/>
<order name="VERKAUFEN" disable="yes"/>
<skill name="armorer" enable="true"/>
<skill name="bow" enable="true"/>
<skill name="building" enable="true"/>
<skill name="cartmaking" enable="true"/>
<skill name="catapult" enable="true"/>
<skill name="crossbow" enable="true"/>
<skill name="forestry" enable="true"/>
<skill name="magic" enable="true"/>
<skill name="melee" enable="true"/>
<skill name="mining" enable="true"/>
<skill name="polearm" enable="true"/>
<skill name="quarrying" enable="true"/>
<skill name="riding" enable="true"/>
<skill name="roadwork" enable="true"/>
<skill name="sailing" enable="true"/>
<skill name="shipcraft" enable="true"/>
<skill name="stamina" enable="true"/>
<skill name="tactics" enable="true"/>
<skill name="training" enable="true"/>
<skill name="unarmed" enable="true"/>
<skill name="weaponsmithing" enable="true"/>
<skill name="herbalism" enable="false"/>
<skill name="alchemy" enable="false"/>
<skill name="entertainment" enable="false"/>
<skill name="espionage" enable="false"/>
<skill name="perception" enable="false"/>
<skill name="stealth" enable="false"/>
<skill name="taxation" enable="false"/>
<skill name="trade" enable="false"/>
<param name="NewbieImmunity" value="9"/> <!-- ends after turn 9 -->
<param name="modules.astralspace" value="0"/>
<param name="modules.wormholes" value="0"/>
<param name="modules.markets" value="1"/>
<!-- resource limitations -->
<param name="magic.regeneration" value="0.75"/>
<param name="magic.power" value="0.5"/>
<param name="resource.factor" value="0.25"/>
<param name="skills.cost.tactics" value="500"/>
<param name="entertain.base" value="0"/>
<param name="entertain.perlevel" value="20"/>
<param name="nmr.timeout" value="5"/>
<param name="nmr.removenewbie" value="0"/>
<param name="GiveRestriction" value="3"/>
<param name="hunger.long" value="0"/>
<param name="hunger.demon.skill" value="1"/>
<param name="hunger.damage" value="1d9+9"/>
<param name="hunger.demons" value="1"/>
<param name="init_spells" value="0"/>
<param name="recruit.allow_merge" value="1"/>
<param name="study.expensivemigrants" value="1"/>
<param name="study.speedup" value="0"/>
<param name="world.era" value="3"/>
<param name="rules.migrants" value="0"/>
<param name="rules.transfermen" value="0"/>
<param name="rules.stealth.faction" value="1"/>
<param name="rules.stealth.anon_battle" value="0"/>
<param name="rules.check_overload" value="0"/>
<param name="rules.combat.goblinbonus" value="3"/>
<param name="rules.ship.damage_drift" value="0.00"/> <!-- percent damage from drifting-->
<param name="rules.alliances" value="1"/>
<param name="rules.combat.herospeed" value="3"/>
<param name="rules.combat.demon_vampire" value="5"/> <!-- regen 1 hp per X points of damage done -->
<param name="rules.combat.skill_bonus" value="0"/>
<!--param name="rules.combat.loot" value="5"/--> <!-- only self + others - keeploot -->
<param name="rules.items.loot_divisor" value="2"/> <!-- damage skims off 1/2 of goods transfers -->
<param name="rules.items.give_divisor" value="2"/> <!-- corruption skims off 1/2 of goods transfers -->
<param name="rules.move.owner_leave" value="1"/> <!-- owner must leave before moving -->
<param name="rules.region_owners" value="1"/>
<param name="rules.cavalry.skill" value="2"/>
<param name="rules.cavalry.mode" value="1"/>
<param name="rules.magic.multipotion" value="1"/>
<param name="rules.magic.wol_effect" value="5"/>
<param name="rules.magic.factionlist" value="1"/>
<param name="rules.magic.wol_type" value="2"/>
<param name="rules.magic.blessed_harvest" value="1"/>
<param name="rules.magic.elfpower" value="1"/> <!-- elves get ring-of-power bonus in a forest -->
<param name="rules.magic.playerschools" value="gwyrrd illaun draig cerddor"/>
<param name="rules.build.other_buildings" value="1"/>
<param name="rules.economy.taxation" value="1"/>
<param name="rules.economy.food" value="2"/>
<param name="rules.economy.wages" value="1"/>
<param name="rules.economy.roqf" value="5"/>
<param name="rules.economy.herbrot" value="0"/>
<param name="rules.dwarf_castles" value="1"/>
<!-- param name="rules.nmr.destroy" value="1"/ -->
<param name="rules.limit.faction" value="250"/>
<!--param name="rules.give" value="15"/ --> <!-- self + peasants + herbs + lux - goods -->
<param name="rules.economy.grow" value="1"/>
<param name="rules.tactics.formula" value="1"/> <!-- 10% per skilldiff -->
<param name="rules.help.mask" value="fight guard money give"/>
<param name="movement.shipspeed.skillbonus" value="6"/>
<param name="alliance.auto" value="fight"/>
<param name="alliance.restricted" value="fight"/>
</game>
<rules>
<function name="wage" value="minimum_wage"/>
</rules>
<strings>
<string name="mailto">
<text locale="de">eressea-server@eressea.de</text>
<text locale="en">eressea-server@eressea.de</text>
</string>
<string name="newbie_info_1">
<text locale="de">Bitte denke daran, deine Befehle mit dem Betreff
E3 BEFEHLE an eressea-server@eressea.de zu senden.</text>
<text locale="en">Remember to send your orders to
eressea-server@eressea.de with the subject E3 ORDERS.</text>
</string>
<string name="mailcmd">
<text locale="de">E3 BEFEHLE</text>
<text locale="en">E3 ORDERS</text>
</string>
</strings>
</eressea>

View File

@ -7,6 +7,37 @@
_a: including article (ein Troll, a troll)
-->
<string name="rm_adamantium">
<text locale="de">Adamantium</text>
<text locale="en">adamantium</text>
</string>
<string name="adamantium">
<text locale="de">Adamantium</text>
<text locale="en">adamantium</text>
</string>
<string name="adamantium_p">
<text locale="de">Adamantium</text>
<text locale="en">adamantium</text>
</string>
<string name="adamantiumaxe">
<text locale="de">Adamantiumaxt</text>
<text locale="en">adamantium axe</text>
</string>
<string name="adamantiumaxe_p">
<text locale="de">Adamantiumäxte</text>
<text locale="en">adamantium axes</text>
</string>
<string name="adamantiumplate">
<text locale="de">Adamantiumrüstung</text>
<text locale="en">adamantium plate</text>
</string>
<string name="adamantiumplate_p">
<text locale="de">Adamantiumrüstungen</text>
<text locale="en">adamantium plates</text>
</string>
<!--Fort-Ausbaustufen -->
<string name="calendar::thirdage">
<text locale="de">des dritten Zeitalters</text>

61
res/e4/main.lua Normal file
View File

@ -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

4
res/e4/modules.lua Normal file
View File

@ -0,0 +1,4 @@
require "spells"
require "e3a.rules"
require "e3a.markets"
require "e3a.frost"

View File

@ -164,36 +164,4 @@
</resource>
<!-- christmas items: end -->
<resource name="adamantium" limited="yes" material="rm_adamantium">
<item weight="200" score="200">
<construction skill="mining" minskill="8" reqsize="1"/>
</item>
<resourcelimit>
<modifier type="require" building="mine"/>
<guard flag="mining"/>
</resourcelimit>
</resource>
<resource name="adamantiumaxe">
<item weight="100" score="500">
<construction skill="weaponsmithing" minskill="8" reqsize="1">
<requirement type="adamantium" quantity="1"/>
<requirement type="log" quantity="1"/>
</construction>
<weapon cut="true" skill="melee" offmod="2" defmod="-2" magres="0.30">
<damage type="rider" value="3d4+15"/>
<damage type="footman" value="3d4+15"/>
</weapon>
</item>
</resource>
<resource name="adamantiumplate">
<item weight="100" score="2000">
<construction skill="armorer" minskill="10" reqsize="1">
<requirement type="adamantium" quantity="3"/>
</construction>
<armor ac="7" penalty="0.1"/>
</item>
</resource>
</resources>

View File

@ -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

View File

@ -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

15
scripts/setup.lua Normal file
View File

@ -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"

100
scripts/tools/build.lua Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,4 @@
read_xml()
eressea.read_game('0.dat')
init_reports()
write_reports()

View File

@ -0,0 +1,2 @@
require "setup"
run_turn()

View File

@ -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 $<TARGET_FILE:eressea> -e run_tests)
add_test(NAME E2 WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/game-e2 COMMAND $<TARGET_FILE:eressea> -e run_tests)
add_test(
NAME E3
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/game-e3
COMMAND $<TARGET_FILE:eressea> runtests.lua
)
add_test(
NAME E2
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/game-e2
COMMAND $<TARGET_FILE:eressea> runtests.lua
)
install(TARGETS eressea DESTINATION bin)

View File

@ -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;
}

View File

@ -15,16 +15,17 @@ without prior permission by the authors of Eressea.
#include "bind_unit.h"
#include "bindings.h"
#include <kernel/config.h>
#include <sqlite3.h>
#include <tolua.h>
#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;
}

View File

@ -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);
}

View File

@ -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
}

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;
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;
id_faction = sqlite3_column_int64(stmt_select, 0);
lastturn = sqlite3_column_int(stmt_select, 6);
f = get_faction_by_id((int)id_faction);
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;
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);
res = sqlite3_step(stmt_select);
if (res != SQLITE_ROW)
break;
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;
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);
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);
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;
id_email = dbstate.id_email;
res = db_update_email(db, f, &dbstate, force, &id_email);
SQL_EXPECT(res, SQLITE_OK);
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);
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]);
id_email = dbstate.id_email;
res = db_update_email(db, f, &dbstate, force, &id_email);
SQL_EXPECT(res, SQLITE_OK);
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);
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]);
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);
}
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)

View File

@ -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! */

View File

@ -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");

View File

@ -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;

View File

@ -54,65 +54,25 @@
#include <limits.h>
#include <assert.h>
/**
** 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 <number> <itemtype>
**/
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 <id> <x> <y>
** 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 <x> <y> <terrain>
** 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 <unit> <x> <y>
** 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 <string>
** 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 <x> <y> <string>
** 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 <id> <string>
** 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 <id> <string>
** 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 <unit> <string>
** 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 <unit> <int> <itemtype>
** 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 <unit> <int> <itemtype>
** 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 <unit> <skill> <tage>
** 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 = &regions;
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;
}

View File

@ -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);

View File

@ -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));

View File

@ -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;
}