forked from github/server
Merge branch 'develop' of github.com:ennorehling/eressea into develop
This commit is contained in:
commit
f2f7ece38c
2
clibs
2
clibs
|
@ -1 +1 @@
|
|||
Subproject commit 1854780fe3073e491775836c22f709668b1fff62
|
||||
Subproject commit 6965050165efdae89305a13bff06283229f143f4
|
|
@ -1 +1 @@
|
|||
Subproject commit 22741d9ce9d19bf7b5f5a219b6ed0925259a4d1b
|
||||
Subproject commit e3533ac0a45e43e9716c40f6a874bc6f41ddc96d
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
if [ -z "$ERESSEA" ]; then
|
||||
echo "You need to define the \$ERESSEA environment variable to run $0"
|
||||
exit -2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
GAME="$ERESSEA/game-$1"
|
||||
|
@ -16,7 +16,7 @@ fi
|
|||
|
||||
if [ ! -d "$GAME/reports" ]; then
|
||||
echo "cannot find reports directory in $GAME"
|
||||
exit -1
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$GAME/reports" || exit
|
||||
|
|
|
@ -11,10 +11,5 @@ fi
|
|||
|
||||
for GAME in $*
|
||||
do
|
||||
if [ "$GAME" == "eressea" ]; then GAME=2 ; fi
|
||||
if [ "$GAME" == "e3a" ]; then GAME=3 ; fi
|
||||
if [ -e $HOME/eressea/game-$GAME/orders.queue ]
|
||||
then
|
||||
$HOME/bin/orders-process $GAME
|
||||
fi
|
||||
$HOME/eressea/orders-php/check-orders.sh $GAME
|
||||
done
|
||||
|
|
|
@ -3,34 +3,9 @@
|
|||
from string import split
|
||||
from string import strip
|
||||
from string import lower
|
||||
import subprocess
|
||||
import bcrypt
|
||||
import sqlite3
|
||||
|
||||
def baseconvert(n, base):
|
||||
"""convert positive decimal integer n to equivalent in another base (2-36)"""
|
||||
|
||||
digits = "0123456789abcdefghijkLmnopqrstuvwxyz"
|
||||
|
||||
try:
|
||||
n = int(n)
|
||||
base = int(base)
|
||||
except:
|
||||
return ""
|
||||
|
||||
if n < 0 or base < 2 or base > 36:
|
||||
return ""
|
||||
|
||||
s = ""
|
||||
while True:
|
||||
r = n % base
|
||||
s = digits[r] + s
|
||||
n = n / base
|
||||
if n == 0:
|
||||
break
|
||||
|
||||
return s
|
||||
|
||||
class EPasswd:
|
||||
def __init__(self):
|
||||
self.data = {}
|
||||
|
@ -47,7 +22,7 @@ class EPasswd:
|
|||
c = conn.cursor()
|
||||
for row in c.execute('SELECT `no`, `email`, `password` FROM `faction`'):
|
||||
(no, email, passwd) = row
|
||||
self.set_data(baseconvert(no, 36), email, passwd)
|
||||
self.set_data(no, email, passwd)
|
||||
conn.close()
|
||||
|
||||
def load_file(self, file):
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# example: orders-accept 2 de < mail.txt
|
||||
|
||||
game="$1"
|
||||
lang="$2"
|
||||
|
||||
[ -z "$ERESSEA" ] && ERESSEA="$HOME/eressea"
|
||||
SCRIPT=$(readlink -f "$0")
|
||||
|
@ -21,5 +22,5 @@ filename=$(basename "$ACCEPT_FILE")
|
|||
email="$ACCEPT_MAIL"
|
||||
if [ -d "$ERESSEA/orders-php" ]
|
||||
then
|
||||
php "$ERESSEA/orders-php/cli.php" insert "$filename" "$email"
|
||||
php "$ERESSEA/orders-php/cli.php" insert "$filename" "$lang" "$email"
|
||||
fi
|
||||
|
|
|
@ -5,7 +5,7 @@ if [ -z "$ERESSEA" ]; then
|
|||
fi
|
||||
if [ ! -f reports.txt ]; then
|
||||
echo "need to run $0 from the report direcory"
|
||||
exit -2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
TEMPLATE=report-mail.txt
|
||||
|
|
|
@ -6,7 +6,7 @@ if [ -z "$ERESSEA" ]; then
|
|||
fi
|
||||
if [ ! -f reports.txt ]; then
|
||||
echo "need to run $0 from the report direcory"
|
||||
exit -2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
PWD=$(pwd)
|
||||
|
@ -33,7 +33,7 @@ fi
|
|||
|
||||
if [ ! -e "$TEMPLATE" ]; then
|
||||
echo "no such email template: $TEMPLATE"
|
||||
exit -3
|
||||
exit 3
|
||||
fi
|
||||
|
||||
while [ -e /tmp/.stopped ] ; do
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
|
||||
if [ -z "$ERESSEA" ]; then
|
||||
echo "You have to define the \$ERESSEA environment variable to run $0"
|
||||
exit -2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
function abort() {
|
||||
if [ $# -gt 0 ]; then
|
||||
echo "$@"
|
||||
fi
|
||||
exit -1
|
||||
exit 1
|
||||
}
|
||||
|
||||
GAME=$1
|
||||
|
@ -47,7 +47,7 @@ fi
|
|||
bash "${FACTION}.sh" "$EMAIL" || reply "Unbekannte Partei $FACTION"
|
||||
|
||||
OWNER=$("$BIN/getfaction.py" "$PWFILE" "$FACTION")
|
||||
if [ ! -z "$OWNER" ]; then
|
||||
if [ -n "$OWNER" ]; then
|
||||
echo "Der Report Deiner Partei wurde an ${EMAIL} gesandt." \
|
||||
| mutt -s "Reportnachforderung Partei ${FACTION}" "$OWNER"
|
||||
fi
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
|
||||
if [ -z "$ERESSEA" ]; then
|
||||
echo "You have to define the \$ERESSEA environment variable to run $0"
|
||||
exit -2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [ ! -z "$1" ]; then
|
||||
if [ -n "$1" ]; then
|
||||
GAME="$ERESSEA/game-$1"
|
||||
else
|
||||
GAME=$ERESSEA
|
||||
|
|
|
@ -4391,6 +4391,13 @@
|
|||
<arg name="command" type="order"/>
|
||||
</type>
|
||||
</message>
|
||||
<message name="error125" section="errors">
|
||||
<type>
|
||||
<arg name="unit" type="unit"/>
|
||||
<arg name="region" type="region"/>
|
||||
<arg name="command" type="order"/>
|
||||
</type>
|
||||
</message>
|
||||
<message name="error84" section="errors">
|
||||
<type>
|
||||
<arg name="unit" type="unit"/>
|
||||
|
@ -5196,6 +5203,12 @@
|
|||
</type>
|
||||
</message>
|
||||
|
||||
<message name="transfer_unit" section="economy">
|
||||
<type>
|
||||
<arg name="unit" type="unit"/>
|
||||
</type>
|
||||
</message>
|
||||
|
||||
<message name="receive_person" section="economy">
|
||||
<type>
|
||||
<arg name="unit" type="unit"/>
|
||||
|
|
|
@ -8,6 +8,13 @@
|
|||
</construction>
|
||||
</ship>
|
||||
|
||||
<ship name="galleon" range="5" storm="1.00" damage="1.00" cargo="2000000" cptskill="5" minskill="2" sumskill="250" opensea="yes">
|
||||
<coast terrain="plain"/>
|
||||
<construction skill="shipcraft" minskill="5" maxsize="2000">
|
||||
<requirement type="log" quantity="1"/>
|
||||
</construction>
|
||||
</ship>
|
||||
|
||||
<ship name="caravel" range="5" storm="1.00" damage="1.00" cargo="300000" cptskill="3" minskill="1" sumskill="30" opensea="yes">
|
||||
<coast terrain="plain"/>
|
||||
<construction skill="shipcraft" minskill="3" maxsize="250">
|
||||
|
|
|
@ -38,6 +38,9 @@ msgstr "\"$unit($mage) verwandelt $unit($target) in $race($race,0).\""
|
|||
msgid "give_person"
|
||||
msgstr "\"$unit($unit) übergibt $int($amount) Person$if($eq($amount,1),\"\",\"en\") an $unit($target).\""
|
||||
|
||||
msgid "transfer_unit"
|
||||
msgstr "\"$unit($unit) schließt sich unserer Partei an.\""
|
||||
|
||||
msgid "rust_effect_2"
|
||||
msgstr "\"$unit($mage) ruft ein fürchterliches Unwetter über seine Feinde. Der magischen Regen lässt alles Eisen rosten.\""
|
||||
|
||||
|
@ -686,6 +689,9 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - Die Einheit kan
|
|||
msgid "error85"
|
||||
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Es wurde keine Emailadresse angegeben.\""
|
||||
|
||||
msgid "error125"
|
||||
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Es wurde kein Banner angegeben.\""
|
||||
|
||||
msgid "starvation"
|
||||
msgstr "\"$unit($unit) verliert in $region($region) $int($dead) von $int($add($live,$dead)) Personen durch Unterernährung.\""
|
||||
|
||||
|
|
|
@ -38,6 +38,9 @@ msgstr "\"$unit($mage) tranforms $unit($target) to $race($race,0).\""
|
|||
msgid "give_person"
|
||||
msgstr "\"$unit($unit) transfers $int($amount) person$if($eq($amount,1),\"\",\"s\") to $unit($target).\""
|
||||
|
||||
msgid "transfer_unit"
|
||||
msgstr "\"$unit($unit) joins our faction.\""
|
||||
|
||||
msgid "rust_effect_2"
|
||||
msgstr "\"$unit($mage) calls forth a terrible torment over the enemy. The magical rain makes all iron rusty.\""
|
||||
|
||||
|
@ -686,6 +689,9 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - The unit cannot
|
|||
msgid "error85"
|
||||
msgstr "\"$unit($unit) in $region($region): '$order($command)' - No email address was supplied.\""
|
||||
|
||||
msgid "error125"
|
||||
msgstr "\"$unit($unit) in $region($region): '$order($command)' - No banner text was supplied.\""
|
||||
|
||||
msgid "starvation"
|
||||
msgstr "\"$unit($unit) loses $int($dead) of $int($add($live,$dead)) people due to starvation in $region($region).\""
|
||||
|
||||
|
|
|
@ -1707,6 +1707,9 @@ msgstr "Ring der Unsichtbarkeit"
|
|||
msgid "caravel_a"
|
||||
msgstr "eine Karavelle"
|
||||
|
||||
msgid "galleon_a"
|
||||
msgstr "eine Galleone"
|
||||
|
||||
msgctxt "keyword"
|
||||
msgid "describe"
|
||||
msgstr "BESCHREIBE"
|
||||
|
@ -2064,6 +2067,9 @@ msgstr "Wir schreiben %s des Monats %s im Jahre %d %s."
|
|||
msgid "caravel"
|
||||
msgstr "Karavelle"
|
||||
|
||||
msgid "galleon"
|
||||
msgstr "Galleone"
|
||||
|
||||
msgid "dragon_postfix_10"
|
||||
msgstr "der Goldene"
|
||||
|
||||
|
|
|
@ -1458,6 +1458,12 @@ msgstr "DESCRIBE"
|
|||
msgid "roi"
|
||||
msgstr "ring of invisibility"
|
||||
|
||||
msgid "galleon_a"
|
||||
msgstr "a galleon"
|
||||
|
||||
msgid "galleon"
|
||||
msgstr "galleon"
|
||||
|
||||
msgid "caravel_a"
|
||||
msgstr "a caravel"
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
#!/bin/sh
|
||||
while [ ! -z $1 ] ; do
|
||||
tmpfile=$(mktemp eressea.XXX)
|
||||
iconv -f latin1 -t utf-8 < $1 | \
|
||||
perl -pe 's/ß/ss/; s/ä/ae/; s/ü/ue/; s/ö/oe/;' \
|
||||
> $tmpfile && \mv $tmpfile $1
|
||||
file $1
|
||||
shift 1
|
||||
done
|
|
@ -37,6 +37,7 @@ function use_snowglobe(u, amount, token, ord)
|
|||
local transform = {
|
||||
ocean = "glacier",
|
||||
firewall = "volcano",
|
||||
activevolcano = "volcano",
|
||||
volcano = "mountain",
|
||||
desert = "plain"
|
||||
}
|
||||
|
|
|
@ -95,11 +95,11 @@ local function write_htpasswd()
|
|||
end
|
||||
|
||||
local function write_files(locales)
|
||||
write_reports()
|
||||
write_summary()
|
||||
write_database()
|
||||
write_passwords()
|
||||
write_htpasswd()
|
||||
write_reports()
|
||||
write_summary()
|
||||
end
|
||||
|
||||
local function write_scores()
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
require 'tests.e2.quit'
|
||||
require 'tests.e2.movement'
|
||||
require 'tests.e2.astral'
|
||||
require 'tests.e2.spells'
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
require "lunit"
|
||||
|
||||
module("tests.e2.quit", package.seeall, lunit.testcase)
|
||||
|
||||
function test_quit_faction()
|
||||
local r = region.create(47, 0, "plain")
|
||||
local f1 = faction.create("human")
|
||||
f1.password = "steamedhams"
|
||||
local f2 = faction.create("human")
|
||||
local u1 = unit.create(f1, r, 8)
|
||||
local u2 = unit.create(f2, r, 9)
|
||||
local u3 = unit.create(f1, r, 10)
|
||||
u1:clear_orders()
|
||||
u2:clear_orders()
|
||||
u1:add_order("STIRB steamedhams PARTEI " .. itoa36(f2.id))
|
||||
u2:add_order("KONTAKTIERE " .. itoa36(u1.id))
|
||||
process_orders()
|
||||
assert_equal(f2, u1.faction)
|
||||
assert_equal(f2, u2.faction)
|
||||
assert_equal(f2, u3.faction)
|
||||
end
|
|
@ -1,4 +1,5 @@
|
|||
-- new tests 2014-06-11
|
||||
require 'tests.laws'
|
||||
require 'tests.faction'
|
||||
require 'tests.locale'
|
||||
require 'tests.movement'
|
||||
|
@ -6,6 +7,5 @@ require 'tests.pool'
|
|||
require 'tests.regions'
|
||||
require 'tests.settings'
|
||||
require 'tests.study'
|
||||
require 'tests.laws'
|
||||
require 'tests.bindings'
|
||||
require 'tests.hunger'
|
||||
|
|
|
@ -70,15 +70,15 @@ end
|
|||
function test_give_temp()
|
||||
u.number = 2
|
||||
u:add_order("GIB TEMP 123 1 PERSON")
|
||||
u:add_order("MACHE TEMP 123 'Herpderp'")
|
||||
u:add_order("MACHE TEMP 123 'Lorax'")
|
||||
u:add_order("ENDE")
|
||||
_G.process_orders()
|
||||
assert_equal(1, u.number)
|
||||
|
||||
for x in f.units do
|
||||
if x.name == 'Herpderp' then u=x end
|
||||
if x.name == 'Lorax' then u=x end
|
||||
end
|
||||
assert_equal('Herpderp', u.name)
|
||||
assert_equal('Lorax', u.name)
|
||||
assert_equal(1, u.number)
|
||||
end
|
||||
|
||||
|
|
|
@ -94,7 +94,9 @@ function test_lighthouse()
|
|||
eressea.free_game()
|
||||
local r = region.create(0, 0, "mountain")
|
||||
local f = faction.create("human", "human@example.com")
|
||||
region.create(1, 0, "mountain")
|
||||
local f2 = faction.create("dwarf")
|
||||
local r2 = region.create(1, 0, "mountain")
|
||||
unit.create(f2, r2, 1).name = 'The Babadook'
|
||||
region.create(2, 0, "ocean")
|
||||
region.create(0, 1, "firewall")
|
||||
region.create(3, 0, "ocean")
|
||||
|
@ -110,12 +112,13 @@ function test_lighthouse()
|
|||
|
||||
init_reports()
|
||||
write_report(f)
|
||||
assert_false(find_in_report(f, " %(1,0%) %(vom Turm erblickt%)"))
|
||||
assert_false(find_in_report(f, "The Babadook"))
|
||||
assert_true(find_in_report(f, " %(1,0%) %(vom Turm erblickt%)"))
|
||||
assert_true(find_in_report(f, " %(2,0%) %(vom Turm erblickt%)"))
|
||||
assert_true(find_in_report(f, " %(3,0%) %(vom Turm erblickt%)"))
|
||||
assert_true(find_in_report(f, " %(0,1%) %(vom Turm erblickt%)"))
|
||||
|
||||
assert_false(find_in_report(f, " %(0,0%) %(vom Turm erblickt%)"))
|
||||
assert_false(find_in_report(f, " %(0,1%) %(vom Turm erblickt%)"))
|
||||
assert_false(find_in_report(f, " %(4,0%) %(vom Turm erblickt%)"))
|
||||
remove_report(f)
|
||||
end
|
||||
|
|
|
@ -101,6 +101,7 @@ set (ERESSEA_SRC
|
|||
creport.c
|
||||
direction.c
|
||||
donations.c
|
||||
recruit.c
|
||||
economy.c
|
||||
eressea.c
|
||||
exparse.c
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2018,
|
||||
Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
110
src/automate.c
110
src/automate.c
|
@ -1,5 +1,6 @@
|
|||
#include <platform.h>
|
||||
|
||||
#include "kernel/config.h"
|
||||
#include "kernel/faction.h"
|
||||
#include "kernel/messages.h"
|
||||
#include "kernel/order.h"
|
||||
|
@ -16,48 +17,57 @@
|
|||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
static int cmp_scholars(const void *lhs, const void *rhs)
|
||||
{
|
||||
static int cmp_scholars(const void *lhs, const void *rhs) {
|
||||
const scholar *a = (const scholar *)lhs;
|
||||
const scholar *b = (const scholar *)rhs;
|
||||
if (a->sk == b->sk) {
|
||||
/* sort by level, descending: */
|
||||
return b->level - a->level;
|
||||
}
|
||||
/* order by skill */
|
||||
return (int)a->sk - (int)b->sk;
|
||||
return b->level - a->level;
|
||||
}
|
||||
|
||||
int autostudy_init(scholar scholars[], int max_scholars, unit **units)
|
||||
int autostudy_init(scholar scholars[], int max_scholars, unit **units, skill_t *o_skill)
|
||||
{
|
||||
unit *unext = NULL, *u = *units;
|
||||
faction *f = u->faction;
|
||||
int nscholars = 0;
|
||||
|
||||
skill_t skill = NOSKILL;
|
||||
while (u) {
|
||||
keyword_t kwd = init_order(u->thisorder, u->faction->locale);
|
||||
if (kwd == K_AUTOSTUDY) {
|
||||
if (long_order_allowed(u)) {
|
||||
if (!fval(u, UFL_MARK)) {
|
||||
keyword_t kwd = init_order(u->thisorder, u->faction->locale);
|
||||
if (kwd == K_AUTOSTUDY) {
|
||||
if (f == u->faction) {
|
||||
scholar * st = scholars + nscholars;
|
||||
skill_t sk = getskill(u->faction->locale);
|
||||
if (check_student(u, u->thisorder, sk)) {
|
||||
st->sk = sk;
|
||||
st->level = effskill_study(u, st->sk);
|
||||
st->learn = 0;
|
||||
st->u = u;
|
||||
if (++nscholars > max_scholars) {
|
||||
log_fatal("you must increase MAXSCHOLARS");
|
||||
unext = u->next;
|
||||
if (long_order_allowed(u)) {
|
||||
scholar * st = scholars + nscholars;
|
||||
skill_t sk = getskill(u->faction->locale);
|
||||
if (skill == NOSKILL && sk != NOSKILL) {
|
||||
skill = sk;
|
||||
if (o_skill) {
|
||||
*o_skill = skill;
|
||||
}
|
||||
}
|
||||
if (check_student(u, u->thisorder, sk)) {
|
||||
if (sk == skill) {
|
||||
fset(u, UFL_MARK);
|
||||
st->level = (short)effskill_study(u, sk);
|
||||
st->learn = 0;
|
||||
st->u = u;
|
||||
if (++nscholars >= max_scholars) {
|
||||
log_warning("you must increase MAXSCHOLARS");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
fset(u, UFL_MARK);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!unext) {
|
||||
unext = u;
|
||||
}
|
||||
}
|
||||
}
|
||||
u = u->next;
|
||||
}
|
||||
while (unext && unext->faction != f) {
|
||||
unext = unext->next;
|
||||
}
|
||||
*units = unext;
|
||||
if (nscholars > 0) {
|
||||
qsort(scholars, nscholars, sizeof(scholar), cmp_scholars);
|
||||
|
@ -81,26 +91,25 @@ void autostudy_run(scholar scholars[], int nscholars)
|
|||
{
|
||||
int ti = 0;
|
||||
while (ti != nscholars) {
|
||||
skill_t sk = scholars[ti].sk;
|
||||
int t, se, ts = 0, tt = 0, si = ti;
|
||||
for (se = ti; se != nscholars && scholars[se].sk == sk; ++se) {
|
||||
for (se = ti; se != nscholars; ++se) {
|
||||
int mint;
|
||||
ts += scholars[se].u->number; /* count total scholars */
|
||||
mint = (ts + 10) / 11; /* need a minimum of ceil(ts/11) teachers */
|
||||
for (; mint > tt && si != nscholars && scholars[si].sk == sk; ++si) {
|
||||
for (; mint > tt && si != nscholars; ++si) {
|
||||
tt += scholars[si].u->number;
|
||||
}
|
||||
}
|
||||
/* now si splits the teachers and students 1:10 */
|
||||
/* first student must be 2 levels below first teacher: */
|
||||
for (; si != se && scholars[si].sk == sk; ++si) {
|
||||
for (; si != se; ++si) {
|
||||
if (scholars[si].level + TEACHDIFFERENCE <= scholars[ti].level) {
|
||||
break;
|
||||
}
|
||||
tt += scholars[si].u->number;
|
||||
}
|
||||
/* now si is the first unit we can teach, if we can teach any */
|
||||
if (si == se || scholars[si].sk != sk) {
|
||||
if (si == se) {
|
||||
/* there are no students, so standard learning for everyone */
|
||||
for (t = ti; t != se; ++t) {
|
||||
learning(scholars + t, scholars[t].u->number);
|
||||
|
@ -161,23 +170,38 @@ void autostudy_run(scholar scholars[], int nscholars)
|
|||
}
|
||||
}
|
||||
|
||||
#define MAXSCHOLARS 512
|
||||
|
||||
void do_autostudy(region *r)
|
||||
{
|
||||
static int config;
|
||||
static int batchsize = MAXSCHOLARS;
|
||||
static int max_scholars;
|
||||
unit *units = r->units;
|
||||
scholar scholars[MAXSCHOLARS];
|
||||
while (units) {
|
||||
int i, nscholars = autostudy_init(scholars, MAXSCHOLARS, &units);
|
||||
if (nscholars > max_scholars) {
|
||||
stats_count("automate.max_scholars", nscholars - max_scholars);
|
||||
max_scholars = nscholars;
|
||||
}
|
||||
autostudy_run(scholars, nscholars);
|
||||
for (i = 0; i != nscholars; ++i) {
|
||||
int days = STUDYDAYS * scholars[i].learn;
|
||||
learn_skill(scholars[i].u, scholars[i].sk, days);
|
||||
unit *u;
|
||||
|
||||
if (config_changed(&config)) {
|
||||
batchsize = config_get_int("automate.batchsize", MAXSCHOLARS);
|
||||
assert(batchsize <= MAXSCHOLARS);
|
||||
}
|
||||
for (u = r->units; u; u = u->next) {
|
||||
if (!fval(u, UFL_MARK)) {
|
||||
unit *ulist = u;
|
||||
int sum_scholars = 0;
|
||||
while (ulist) {
|
||||
skill_t skill = NOSKILL;
|
||||
int i, nscholars = autostudy_init(scholars, batchsize, &ulist, &skill);
|
||||
assert(ulist == NULL || ulist->faction == u->faction);
|
||||
sum_scholars += nscholars;
|
||||
if (sum_scholars > max_scholars) {
|
||||
stats_count("automate.max_scholars", sum_scholars - max_scholars);
|
||||
max_scholars = sum_scholars;
|
||||
}
|
||||
autostudy_run(scholars, nscholars);
|
||||
for (i = 0; i != nscholars; ++i) {
|
||||
int days = STUDYDAYS * scholars[i].learn;
|
||||
learn_skill(scholars[i].u, skill, days);
|
||||
}
|
||||
}
|
||||
}
|
||||
freset(u, UFL_MARK);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,16 +28,16 @@ struct unit;
|
|||
|
||||
typedef struct scholar {
|
||||
struct unit *u;
|
||||
skill_t sk;
|
||||
int level;
|
||||
int learn;
|
||||
short level;
|
||||
} scholar;
|
||||
|
||||
#define MAXSCHOLARS 128
|
||||
#define STUDENTS_PER_TEACHER 10
|
||||
|
||||
void do_autostudy(struct region *r);
|
||||
|
||||
int autostudy_init(scholar scholars[], int max_scholars, struct unit **units);
|
||||
int autostudy_init(scholar scholars[], int max_scholars, struct unit **units, skill_t *o_skill);
|
||||
void autostudy_run(scholar scholars[], int nscholars);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "automate.h"
|
||||
|
||||
#include "kernel/config.h"
|
||||
#include "kernel/faction.h"
|
||||
#include "kernel/order.h"
|
||||
#include "kernel/region.h"
|
||||
|
@ -20,6 +21,8 @@ static void test_autostudy_init(CuTest *tc) {
|
|||
unit *u1, *u2, *u3, *u4, *u5, *ulist;
|
||||
faction *f;
|
||||
region *r;
|
||||
message *msg;
|
||||
skill_t skill = NOSKILL;
|
||||
|
||||
test_setup();
|
||||
mt_create_error(77);
|
||||
|
@ -33,36 +36,36 @@ static void test_autostudy_init(CuTest *tc) {
|
|||
u2 = test_create_unit(f, r);
|
||||
u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]);
|
||||
set_level(u2, SK_ENTERTAINMENT, 2);
|
||||
u3 = test_create_unit(f, r);
|
||||
u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
|
||||
u4 = test_create_unit(f, r);
|
||||
u4->thisorder = create_order(K_AUTOSTUDY, f->locale, "Dudelidu");
|
||||
u3 = test_create_unit(f, r);
|
||||
u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
|
||||
u5 = test_create_unit(test_create_faction(NULL), r);
|
||||
u5->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
|
||||
scholars[3].u = NULL;
|
||||
scholars[2].u = NULL;
|
||||
|
||||
ulist = r->units;
|
||||
CuAssertIntEquals(tc, 3, autostudy_init(scholars, 4, &ulist));
|
||||
CuAssertPtrNotNull(tc, test_find_messagetype(u4->faction->msgs, "error77"));
|
||||
CuAssertIntEquals(tc, 2, autostudy_init(scholars, 4, &ulist, &skill));
|
||||
CuAssertIntEquals(tc, SK_ENTERTAINMENT, skill);
|
||||
CuAssertPtrEquals(tc, u2, scholars[0].u);
|
||||
CuAssertIntEquals(tc, 2, scholars[0].level);
|
||||
CuAssertIntEquals(tc, 0, scholars[0].learn);
|
||||
CuAssertIntEquals(tc, SK_ENTERTAINMENT, scholars[0].sk);
|
||||
CuAssertPtrEquals(tc, u1, scholars[1].u);
|
||||
CuAssertIntEquals(tc, 0, scholars[1].level);
|
||||
CuAssertIntEquals(tc, 0, scholars[1].learn);
|
||||
CuAssertIntEquals(tc, SK_ENTERTAINMENT, scholars[1].sk);
|
||||
CuAssertPtrEquals(tc, u3, scholars[2].u);
|
||||
CuAssertIntEquals(tc, 0, scholars[2].level);
|
||||
CuAssertIntEquals(tc, 0, scholars[2].learn);
|
||||
CuAssertIntEquals(tc, SK_PERCEPTION, scholars[2].sk);
|
||||
CuAssertPtrEquals(tc, NULL, scholars[3].u);
|
||||
CuAssertPtrEquals(tc, u5, ulist);
|
||||
CuAssertIntEquals(tc, 1, autostudy_init(scholars, 4, &ulist));
|
||||
CuAssertPtrEquals(tc, u5, scholars[0].u);
|
||||
CuAssertPtrEquals(tc, NULL, scholars[2].u);
|
||||
CuAssertPtrEquals(tc, NULL, ulist);
|
||||
|
||||
ulist = u3;
|
||||
CuAssertIntEquals(tc, 1, autostudy_init(scholars, 4, &ulist, &skill));
|
||||
CuAssertIntEquals(tc, SK_PERCEPTION, skill);
|
||||
CuAssertPtrEquals(tc, u3, scholars[0].u);
|
||||
CuAssertIntEquals(tc, 0, scholars[0].level);
|
||||
CuAssertIntEquals(tc, 0, scholars[0].learn);
|
||||
CuAssertIntEquals(tc, SK_PERCEPTION, scholars[0].sk);
|
||||
CuAssertPtrEquals(tc, NULL, ulist);
|
||||
|
||||
CuAssertPtrNotNull(tc, msg = test_find_messagetype(f->msgs, "error77"));
|
||||
CuAssertPtrEquals(tc, NULL, test_find_messagetype_ex(f->msgs, "error77", msg));
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
|
@ -75,6 +78,7 @@ static void test_autostudy_run_twoteachers(CuTest *tc) {
|
|||
unit *u1, *u2, *u3, *u4, *ulist;
|
||||
faction *f;
|
||||
region *r;
|
||||
skill_t skill;
|
||||
|
||||
test_setup();
|
||||
r = test_create_plain(0, 0);
|
||||
|
@ -94,9 +98,10 @@ static void test_autostudy_run_twoteachers(CuTest *tc) {
|
|||
set_number(u4, 12);
|
||||
|
||||
ulist = r->units;
|
||||
CuAssertIntEquals(tc, 4, nscholars = autostudy_init(scholars, 4, &ulist));
|
||||
CuAssertIntEquals(tc, 4, nscholars = autostudy_init(scholars, 4, &ulist, &skill));
|
||||
CuAssertPtrEquals(tc, NULL, ulist);
|
||||
autostudy_run(scholars, nscholars);
|
||||
CuAssertIntEquals(tc, SK_ENTERTAINMENT, skill);
|
||||
CuAssertIntEquals(tc, 0, scholars[0].learn);
|
||||
CuAssertIntEquals(tc, 0, scholars[1].learn);
|
||||
CuAssertIntEquals(tc, scholars[2].u->number * 2, scholars[2].learn);
|
||||
|
@ -125,21 +130,34 @@ static void test_autostudy_run(CuTest *tc) {
|
|||
u3 = test_create_unit(f, r);
|
||||
u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
|
||||
set_number(u3, 15);
|
||||
scholars[3].u = NULL;
|
||||
|
||||
scholars[2].u = NULL;
|
||||
ulist = r->units;
|
||||
CuAssertIntEquals(tc, 3, nscholars = autostudy_init(scholars, 4, &ulist));
|
||||
CuAssertIntEquals(tc, 2, nscholars = autostudy_init(scholars, 4, &ulist, NULL));
|
||||
CuAssertIntEquals(tc, UFL_MARK, u1->flags & UFL_MARK);
|
||||
CuAssertIntEquals(tc, UFL_MARK, u2->flags & UFL_MARK);
|
||||
CuAssertIntEquals(tc, 0, u3->flags & UFL_MARK);
|
||||
CuAssertPtrEquals(tc, NULL, ulist);
|
||||
autostudy_run(scholars, nscholars);
|
||||
CuAssertIntEquals(tc, 1, scholars[0].learn);
|
||||
CuAssertIntEquals(tc, 20, scholars[1].learn);
|
||||
CuAssertIntEquals(tc, 15, scholars[2].learn);
|
||||
CuAssertPtrEquals(tc, NULL, scholars[2].u);
|
||||
|
||||
scholars[1].u = NULL;
|
||||
ulist = u3;
|
||||
CuAssertIntEquals(tc, 1, nscholars = autostudy_init(scholars, 4, &ulist, NULL));
|
||||
CuAssertPtrEquals(tc, NULL, ulist);
|
||||
autostudy_run(scholars, nscholars);
|
||||
CuAssertIntEquals(tc, 15, scholars[0].learn);
|
||||
CuAssertPtrEquals(tc, NULL, scholars[1].u);
|
||||
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_autostudy_run_noteachers(CuTest *tc) {
|
||||
scholar scholars[4];
|
||||
int nscholars;
|
||||
unit *u1, *u2, *u3, *ulist;
|
||||
unit *u1, *u2, *ulist;
|
||||
faction *f;
|
||||
region *r;
|
||||
|
||||
|
@ -147,23 +165,24 @@ static void test_autostudy_run_noteachers(CuTest *tc) {
|
|||
r = test_create_plain(0, 0);
|
||||
f = test_create_faction(NULL);
|
||||
u1 = test_create_unit(f, r);
|
||||
u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_LUMBERJACK]);
|
||||
set_number(u1, 2);
|
||||
u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]);
|
||||
set_number(u1, 5);
|
||||
set_level(u1, SK_ENTERTAINMENT, 2);
|
||||
|
||||
u2 = test_create_unit(f, r);
|
||||
u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]);
|
||||
set_number(u2, 10);
|
||||
u3 = test_create_unit(f, r);
|
||||
u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
|
||||
set_number(u3, 15);
|
||||
scholars[3].u = NULL;
|
||||
set_number(u2, 7);
|
||||
set_level(u2, SK_ENTERTAINMENT, 2);
|
||||
|
||||
scholars[2].u = NULL;
|
||||
ulist = r->units;
|
||||
CuAssertIntEquals(tc, 3, nscholars = autostudy_init(scholars, 4, &ulist));
|
||||
CuAssertIntEquals(tc, 2, nscholars = autostudy_init(scholars, 4, &ulist, NULL));
|
||||
CuAssertPtrEquals(tc, NULL, ulist);
|
||||
autostudy_run(scholars, nscholars);
|
||||
CuAssertIntEquals(tc, 2, scholars[0].learn);
|
||||
CuAssertIntEquals(tc, 10, scholars[1].learn);
|
||||
CuAssertIntEquals(tc, 15, scholars[2].learn);
|
||||
/* stupid qsort is unstable: */
|
||||
CuAssertIntEquals(tc, 12, scholars[0].learn + scholars[1].learn);
|
||||
CuAssertIntEquals(tc, 35, scholars[0].learn * scholars[1].learn);
|
||||
CuAssertPtrEquals(tc, NULL, scholars[2].u);
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
|
@ -188,7 +207,7 @@ static void test_autostudy_run_teachers_learn(CuTest *tc) {
|
|||
u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]);
|
||||
set_number(u2, 10);
|
||||
ulist = r->units;
|
||||
CuAssertIntEquals(tc, 2, nscholars = autostudy_init(scholars, 4, &ulist));
|
||||
CuAssertIntEquals(tc, 2, nscholars = autostudy_init(scholars, 4, &ulist, NULL));
|
||||
CuAssertPtrEquals(tc, NULL, ulist);
|
||||
autostudy_run(scholars, nscholars);
|
||||
CuAssertIntEquals(tc, 1, scholars[0].learn);
|
||||
|
@ -222,7 +241,7 @@ static void test_autostudy_run_skilldiff(CuTest *tc) {
|
|||
set_number(u3, 10);
|
||||
scholars[3].u = NULL;
|
||||
ulist = r->units;
|
||||
CuAssertIntEquals(tc, 3, nscholars = autostudy_init(scholars, 4, &ulist));
|
||||
CuAssertIntEquals(tc, 3, nscholars = autostudy_init(scholars, 4, &ulist, NULL));
|
||||
CuAssertPtrEquals(tc, NULL, ulist);
|
||||
autostudy_run(scholars, nscholars);
|
||||
CuAssertIntEquals(tc, 0, scholars[0].learn);
|
||||
|
@ -231,11 +250,78 @@ static void test_autostudy_run_skilldiff(CuTest *tc) {
|
|||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_autostudy_batches(CuTest *tc) {
|
||||
scholar scholars[2];
|
||||
int nscholars;
|
||||
unit *u1, *u2, *u3, *ulist;
|
||||
faction *f;
|
||||
region *r;
|
||||
|
||||
test_setup();
|
||||
r = test_create_plain(0, 0);
|
||||
f = test_create_faction(NULL);
|
||||
u1 = test_create_unit(f, r);
|
||||
u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
|
||||
set_number(u1, 1);
|
||||
set_level(u1, SK_PERCEPTION, 2);
|
||||
u2 = test_create_unit(f, r);
|
||||
u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
|
||||
set_number(u2, 10);
|
||||
u3 = test_create_unit(f, r);
|
||||
u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
|
||||
set_number(u3, 10);
|
||||
scholars[1].u = NULL;
|
||||
ulist = r->units;
|
||||
config_set("automate.batchsize", "2");
|
||||
CuAssertIntEquals(tc, 2, nscholars = autostudy_init(scholars, 2, &ulist, NULL));
|
||||
CuAssertPtrEquals(tc, u3, ulist);
|
||||
autostudy_run(scholars, nscholars);
|
||||
CuAssertIntEquals(tc, 0, scholars[0].learn);
|
||||
CuAssertIntEquals(tc, 20, scholars[1].learn);
|
||||
CuAssertIntEquals(tc, 1, nscholars = autostudy_init(scholars, 2, &ulist, NULL));
|
||||
autostudy_run(scholars, nscholars);
|
||||
CuAssertIntEquals(tc, 10, scholars[0].learn);
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_do_autostudy(CuTest *tc) {
|
||||
unit *u1, *u2, *u3, *u4;
|
||||
faction *f;
|
||||
region *r;
|
||||
|
||||
test_setup();
|
||||
r = test_create_plain(0, 0);
|
||||
f = test_create_faction(NULL);
|
||||
u1 = test_create_unit(f, r);
|
||||
u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
|
||||
set_number(u1, 1);
|
||||
set_level(u1, SK_PERCEPTION, 2);
|
||||
u2 = test_create_unit(f, r);
|
||||
u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
|
||||
set_number(u2, 10);
|
||||
u3 = test_create_unit(f, r);
|
||||
u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]);
|
||||
u4 = test_create_unit(test_create_faction(NULL), r);
|
||||
u4->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]);
|
||||
do_autostudy(r);
|
||||
CuAssertIntEquals(tc, 2, get_level(u1, SK_PERCEPTION));
|
||||
/* impossible to say if u2 is T1 or T2 now */
|
||||
CuAssertIntEquals(tc, 1, get_level(u3, SK_ENTERTAINMENT));
|
||||
CuAssertIntEquals(tc, 1, get_level(u4, SK_ENTERTAINMENT));
|
||||
CuAssertIntEquals(tc, 0, u1->flags & UFL_MARK);
|
||||
CuAssertIntEquals(tc, 0, u2->flags & UFL_MARK);
|
||||
CuAssertIntEquals(tc, 0, u3->flags & UFL_MARK);
|
||||
CuAssertIntEquals(tc, 0, u4->flags & UFL_MARK);
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
CuSuite *get_automate_suite(void)
|
||||
{
|
||||
CuSuite *suite = CuSuiteNew();
|
||||
SUITE_ADD_TEST(suite, test_autostudy_init);
|
||||
SUITE_ADD_TEST(suite, test_autostudy_run);
|
||||
SUITE_ADD_TEST(suite, test_do_autostudy);
|
||||
SUITE_ADD_TEST(suite, test_autostudy_batches);
|
||||
SUITE_ADD_TEST(suite, test_autostudy_run_noteachers);
|
||||
SUITE_ADD_TEST(suite, test_autostudy_run_teachers_learn);
|
||||
SUITE_ADD_TEST(suite, test_autostudy_run_twoteachers);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -527,7 +527,7 @@ static int tolua_region_get_peasants(lua_State * L)
|
|||
region *self = (region *)tolua_tousertype(L, 1, NULL);
|
||||
|
||||
if (self) {
|
||||
lua_pushinteger(L, self->land ? self->land->peasants : 0);
|
||||
lua_pushinteger(L, rpeasants(self));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
@ -538,7 +538,7 @@ static int tolua_region_set_peasants(lua_State * L)
|
|||
region *self = (region *)tolua_tousertype(L, 1, NULL);
|
||||
|
||||
if (self && self->land) {
|
||||
self->land->peasants = lua_tointeger(L, 2);
|
||||
rsetpeasants(self, lua_tointeger(L, 2));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -115,13 +115,13 @@ int contact_cmd(unit * u, order * ord)
|
|||
}
|
||||
else {
|
||||
/* old-style syntax, KONTAKTIERE foo */
|
||||
unit *u2;
|
||||
unit *u2 = NULL;
|
||||
int n = 0;
|
||||
if (p == P_TEMP) {
|
||||
n = getid();
|
||||
u2 = findnewunit(u->region, u->faction, n);
|
||||
}
|
||||
else {
|
||||
else if (str) {
|
||||
n = atoi36((const char *)str);
|
||||
u2 = findunit(n);
|
||||
}
|
||||
|
|
|
@ -74,11 +74,73 @@ static void test_contact_cmd(CuTest *tc) {
|
|||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_contact_cmd_invalid(CuTest *tc) {
|
||||
struct unit *u;
|
||||
struct region *r;
|
||||
const struct locale *lang;
|
||||
struct order *ord;
|
||||
|
||||
test_setup();
|
||||
r = test_create_plain(0, 0);
|
||||
u = test_create_unit(test_create_faction(NULL), r);
|
||||
lang = u->faction->locale;
|
||||
|
||||
/* KONTAKTIERE EINHEIT <not-found> */
|
||||
ord = create_order(K_CONTACT, u->faction->locale, "%s %i",
|
||||
LOC(lang, parameters[P_UNIT]), u->no + 1);
|
||||
contact_cmd(u, ord);
|
||||
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "feedback_unit_not_found"));
|
||||
free_order(ord);
|
||||
test_clear_messages(u->faction);
|
||||
|
||||
/* KONTAKTIERE EINHEIT TEMP <not-found> */
|
||||
ord = create_order(K_CONTACT, u->faction->locale, "%s %s %i",
|
||||
LOC(lang, parameters[P_UNIT]), LOC(lang, parameters[P_TEMP]), u->no + 1);
|
||||
contact_cmd(u, ord);
|
||||
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "feedback_unit_not_found"));
|
||||
free_order(ord);
|
||||
test_clear_messages(u->faction);
|
||||
|
||||
/* KONTAKTIERE EINHEIT TEMP */
|
||||
ord = create_order(K_CONTACT, u->faction->locale, "%s %s",
|
||||
LOC(lang, parameters[P_UNIT]), LOC(lang, parameters[P_TEMP]));
|
||||
contact_cmd(u, ord);
|
||||
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "feedback_unit_not_found"));
|
||||
free_order(ord);
|
||||
test_clear_messages(u->faction);
|
||||
|
||||
/* KONTAKTIERE EINHEIT */
|
||||
ord = create_order(K_CONTACT, u->faction->locale,
|
||||
LOC(lang, parameters[P_UNIT]));
|
||||
contact_cmd(u, ord);
|
||||
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "feedback_unit_not_found"));
|
||||
free_order(ord);
|
||||
test_clear_messages(u->faction);
|
||||
|
||||
/* KONTAKTIERE TEMP */
|
||||
ord = create_order(K_CONTACT, u->faction->locale,
|
||||
LOC(lang, parameters[P_TEMP]));
|
||||
contact_cmd(u, ord);
|
||||
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "feedback_unit_not_found"));
|
||||
free_order(ord);
|
||||
test_clear_messages(u->faction);
|
||||
|
||||
/* KONTAKTIERE */
|
||||
ord = create_order(K_CONTACT, u->faction->locale, NULL);
|
||||
contact_cmd(u, ord);
|
||||
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "feedback_unit_not_found"));
|
||||
free_order(ord);
|
||||
test_clear_messages(u->faction);
|
||||
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
CuSuite *get_contact_suite(void)
|
||||
{
|
||||
CuSuite *suite = CuSuiteNew();
|
||||
SUITE_ADD_TEST(suite, test_contact);
|
||||
SUITE_ADD_TEST(suite, test_contact_cmd);
|
||||
SUITE_ADD_TEST(suite, test_contact_cmd_invalid);
|
||||
|
||||
return suite;
|
||||
}
|
||||
|
|
|
@ -1503,33 +1503,34 @@ static void cr_output_region(FILE * F, report_context * ctx, region * r)
|
|||
cr_output_messages(F, mlist, f);
|
||||
}
|
||||
}
|
||||
/* buildings */
|
||||
for (b = rbuildings(r); b; b = b->next) {
|
||||
int fno = -1;
|
||||
u = building_owner(b);
|
||||
if (u && !fval(u, UFL_ANON_FACTION)) {
|
||||
const faction *sf = visible_faction(f, u);
|
||||
fno = sf->no;
|
||||
}
|
||||
cr_output_building_compat(F, b, u, fno, f);
|
||||
}
|
||||
|
||||
/* ships */
|
||||
for (sh = r->ships; sh; sh = sh->next) {
|
||||
int fno = -1;
|
||||
u = ship_owner(sh);
|
||||
if (u && !fval(u, UFL_ANON_FACTION)) {
|
||||
const faction *sf = visible_faction(f, u);
|
||||
fno = sf->no;
|
||||
if (r->seen.mode >= seen_lighthouse) {
|
||||
/* buildings */
|
||||
for (b = rbuildings(r); b; b = b->next) {
|
||||
int fno = -1;
|
||||
u = building_owner(b);
|
||||
if (u && !fval(u, UFL_ANON_FACTION)) {
|
||||
const faction *sf = visible_faction(f, u);
|
||||
fno = sf->no;
|
||||
}
|
||||
cr_output_building_compat(F, b, u, fno, f);
|
||||
}
|
||||
|
||||
cr_output_ship_compat(F, sh, u, fno, f, r);
|
||||
}
|
||||
/* ships */
|
||||
for (sh = r->ships; sh; sh = sh->next) {
|
||||
int fno = -1;
|
||||
u = ship_owner(sh);
|
||||
if (u && !fval(u, UFL_ANON_FACTION)) {
|
||||
const faction *sf = visible_faction(f, u);
|
||||
fno = sf->no;
|
||||
}
|
||||
|
||||
/* visible units */
|
||||
for (u = r->units; u; u = u->next) {
|
||||
if (visible_unit(u, f, stealthmod, r->seen.mode)) {
|
||||
cr_output_unit_compat(F, f, u, r->seen.mode);
|
||||
cr_output_ship_compat(F, sh, u, fno, f, r);
|
||||
}
|
||||
/* visible units */
|
||||
for (u = r->units; u; u = u->next) {
|
||||
if (visible_unit(u, f, stealthmod, r->seen.mode)) {
|
||||
cr_output_unit_compat(F, f, u, r->seen.mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
653
src/economy.c
653
src/economy.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2014,
|
||||
Copyright (c) 1998-2019,
|
||||
Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
@ -84,28 +84,28 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
static int working;
|
||||
|
||||
static econ_request entertainers[1024];
|
||||
static econ_request *nextentertainer;
|
||||
static int entertaining;
|
||||
#define MAX_REQUESTS 1024
|
||||
static struct econ_request econ_requests[MAX_REQUESTS];
|
||||
|
||||
static econ_request **g_requests; /* TODO: no need for this to be module-global */
|
||||
|
||||
#define RECRUIT_MERGE 1
|
||||
static int rules_recruit = -1;
|
||||
#define ENTERTAINFRACTION 20
|
||||
|
||||
static void recruit_init(void)
|
||||
{
|
||||
if (rules_recruit < 0) {
|
||||
rules_recruit = 0;
|
||||
if (config_get_int("recruit.allow_merge", 1)) {
|
||||
rules_recruit |= RECRUIT_MERGE;
|
||||
}
|
||||
}
|
||||
static void add_request(econ_request * req, enum econ_type type, unit *u, order *ord, int want) {
|
||||
req->next = NULL;
|
||||
req->unit = u;
|
||||
req->qty = u->wants = want;
|
||||
req->type = type;
|
||||
}
|
||||
|
||||
#define ENTERTAINFRACTION 20
|
||||
static bool rule_auto_taxation(void)
|
||||
{
|
||||
return config_get_int("rules.economy.taxation", 0) != 0;
|
||||
}
|
||||
|
||||
static bool rule_autowork(void) {
|
||||
return config_get_int("work.auto", 0) != 0;
|
||||
}
|
||||
|
||||
int entertainmoney(const region * r)
|
||||
{
|
||||
|
@ -187,6 +187,10 @@ int expand_production(region * r, econ_request * requests, econ_request ***resul
|
|||
return norders;
|
||||
}
|
||||
|
||||
static int expandorders(region * r, econ_request * requests) {
|
||||
return expand_production(r, requests, &g_requests);
|
||||
}
|
||||
|
||||
static void free_requests(econ_request *requests) {
|
||||
while (requests) {
|
||||
econ_request *req = requests->next;
|
||||
|
@ -195,383 +199,8 @@ static void free_requests(econ_request *requests) {
|
|||
}
|
||||
}
|
||||
|
||||
static int expandorders(region * r, econ_request * requests) {
|
||||
return expand_production(r, requests, &g_requests);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
typedef struct recruitment {
|
||||
struct recruitment *next;
|
||||
faction *f;
|
||||
econ_request *requests;
|
||||
int total, assigned;
|
||||
} recruitment;
|
||||
|
||||
/** Creates a list of recruitment structs, one for each faction. Adds every quantifyable production
|
||||
* to the faction's struct and to total.
|
||||
*/
|
||||
static recruitment *select_recruitment(econ_request ** rop,
|
||||
int(*quantify) (const struct race *, int), int *total)
|
||||
{
|
||||
recruitment *recruits = NULL;
|
||||
|
||||
while (*rop) {
|
||||
recruitment *rec = recruits;
|
||||
econ_request *ro = *rop;
|
||||
unit *u = ro->unit;
|
||||
const race *rc = u_race(u);
|
||||
int qty = quantify(rc, ro->qty);
|
||||
|
||||
if (qty < 0) {
|
||||
rop = &ro->next; /* skip this one */
|
||||
}
|
||||
else {
|
||||
*rop = ro->next; /* remove this one */
|
||||
while (rec && rec->f != u->faction)
|
||||
rec = rec->next;
|
||||
if (rec == NULL) {
|
||||
rec = (recruitment *)malloc(sizeof(recruitment));
|
||||
if (!rec) abort();
|
||||
rec->f = u->faction;
|
||||
rec->total = 0;
|
||||
rec->assigned = 0;
|
||||
rec->requests = NULL;
|
||||
rec->next = recruits;
|
||||
recruits = rec;
|
||||
}
|
||||
*total += qty;
|
||||
rec->total += qty;
|
||||
ro->next = rec->requests;
|
||||
rec->requests = ro;
|
||||
}
|
||||
}
|
||||
return recruits;
|
||||
}
|
||||
|
||||
void add_recruits(unit * u, int number, int wanted)
|
||||
{
|
||||
region *r = u->region;
|
||||
assert(number <= wanted);
|
||||
if (number > 0) {
|
||||
unit *unew;
|
||||
char equipment[64];
|
||||
int len;
|
||||
|
||||
if (u->number == 0) {
|
||||
set_number(u, number);
|
||||
u->hp = number * unit_max_hp(u);
|
||||
unew = u;
|
||||
}
|
||||
else {
|
||||
unew = create_unit(r, u->faction, number, u_race(u), 0, NULL, u);
|
||||
}
|
||||
|
||||
len = snprintf(equipment, sizeof(equipment), "new_%s", u_race(u)->_name);
|
||||
if (len > 0 && (size_t)len < sizeof(equipment)) {
|
||||
equip_unit(unew, equipment);
|
||||
}
|
||||
if (unew != u) {
|
||||
transfermen(unew, u, unew->number);
|
||||
remove_unit(&r->units, unew);
|
||||
}
|
||||
}
|
||||
if (number < wanted) {
|
||||
ADDMSG(&u->faction->msgs, msg_message("recruit",
|
||||
"unit region amount want", u, r, number, wanted));
|
||||
}
|
||||
}
|
||||
|
||||
static int any_recruiters(const struct race *rc, int qty)
|
||||
{
|
||||
return (int)(qty * 2 * rc->recruit_multi);
|
||||
}
|
||||
|
||||
static int do_recruiting(recruitment * recruits, int available)
|
||||
{
|
||||
recruitment *rec;
|
||||
int recruited = 0;
|
||||
|
||||
/* try to assign recruits to factions fairly */
|
||||
while (available > 0) {
|
||||
int n = 0;
|
||||
int rest, mintotal = INT_MAX;
|
||||
|
||||
/* find smallest production */
|
||||
for (rec = recruits; rec != NULL; rec = rec->next) {
|
||||
int want = rec->total - rec->assigned;
|
||||
if (want > 0) {
|
||||
if (mintotal > want)
|
||||
mintotal = want;
|
||||
++n;
|
||||
}
|
||||
}
|
||||
if (n == 0)
|
||||
break;
|
||||
if (mintotal * n > available) {
|
||||
mintotal = available / n;
|
||||
}
|
||||
rest = available - mintotal * n;
|
||||
|
||||
/* assign size of smallest production for everyone if possible; in the end roll dice to assign
|
||||
* small rest */
|
||||
for (rec = recruits; rec != NULL; rec = rec->next) {
|
||||
int want = rec->total - rec->assigned;
|
||||
|
||||
if (want > 0) {
|
||||
int get = mintotal;
|
||||
if (want > mintotal && rest < n && (rng_int() % n) < rest) {
|
||||
--rest;
|
||||
++get;
|
||||
}
|
||||
assert(get <= want);
|
||||
available -= get;
|
||||
rec->assigned += get;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* do actual recruiting */
|
||||
for (rec = recruits; rec != NULL; rec = rec->next) {
|
||||
econ_request *req;
|
||||
int get = rec->assigned;
|
||||
|
||||
for (req = rec->requests; req; req = req->next) {
|
||||
unit *u = req->unit;
|
||||
const race *rc = u_race(u); /* race is set in recruit() */
|
||||
int number;
|
||||
double multi = 2.0 * rc->recruit_multi;
|
||||
|
||||
number = (int)(get / multi);
|
||||
if (number > req->qty) number = req->qty;
|
||||
if (rc->recruitcost) {
|
||||
int afford = get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT,
|
||||
number * rc->recruitcost) / rc->recruitcost;
|
||||
if (number > afford) number = afford;
|
||||
}
|
||||
if (u->number + number > UNIT_MAXSIZE) {
|
||||
ADDMSG(&u->faction->msgs, msg_feedback(u, req->type.recruit.ord, "error_unit_size",
|
||||
"maxsize", UNIT_MAXSIZE));
|
||||
number = UNIT_MAXSIZE - u->number;
|
||||
assert(number >= 0);
|
||||
}
|
||||
if (rc->recruitcost) {
|
||||
use_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT,
|
||||
rc->recruitcost * number);
|
||||
}
|
||||
if (u->number == 0 && fval(u, UFL_DEAD)) {
|
||||
/* unit is empty, dead, and cannot recruit */
|
||||
number = 0;
|
||||
}
|
||||
add_recruits(u, number, req->qty);
|
||||
if (number > 0) {
|
||||
int dec = (int)(number * multi);
|
||||
if ((rc->ec_flags & ECF_REC_ETHEREAL) == 0) {
|
||||
recruited += dec;
|
||||
}
|
||||
|
||||
get -= dec;
|
||||
}
|
||||
}
|
||||
}
|
||||
return recruited;
|
||||
}
|
||||
|
||||
void free_recruitments(recruitment * recruits)
|
||||
{
|
||||
while (recruits) {
|
||||
recruitment *rec = recruits;
|
||||
recruits = rec->next;
|
||||
while (rec->requests) {
|
||||
econ_request *req = rec->requests;
|
||||
rec->requests = req->next;
|
||||
free(req);
|
||||
}
|
||||
free(rec);
|
||||
}
|
||||
}
|
||||
|
||||
/* Rekrutierung */
|
||||
static void expandrecruit(region * r, econ_request * recruitorders)
|
||||
{
|
||||
recruitment *recruits;
|
||||
int orc_total = 0;
|
||||
|
||||
/* peasant limited: */
|
||||
recruits = select_recruitment(&recruitorders, any_recruiters, &orc_total);
|
||||
if (recruits) {
|
||||
int orc_recruited, orc_peasants = rpeasants(r) * 2;
|
||||
int orc_frac = orc_peasants / RECRUITFRACTION; /* anzahl orks. 2 ork = 1 bauer */
|
||||
if (orc_total < orc_frac)
|
||||
orc_frac = orc_total;
|
||||
orc_recruited = do_recruiting(recruits, orc_frac);
|
||||
assert(orc_recruited <= orc_frac);
|
||||
rsetpeasants(r, (orc_peasants - orc_recruited) / 2);
|
||||
free_recruitments(recruits);
|
||||
}
|
||||
|
||||
/* no limit: */
|
||||
recruits = select_recruitment(&recruitorders, any_recruiters, &orc_total);
|
||||
if (recruits) {
|
||||
int recruited, peasants = rpeasants(r) * 2;
|
||||
recruited = do_recruiting(recruits, INT_MAX);
|
||||
if (recruited > 0) {
|
||||
rsetpeasants(r, (peasants - recruited) / 2);
|
||||
}
|
||||
free_recruitments(recruits);
|
||||
}
|
||||
|
||||
assert(recruitorders == NULL);
|
||||
}
|
||||
|
||||
static int recruit_cost(const faction * f, const race * rc)
|
||||
{
|
||||
if (is_monsters(f) || valid_race(f, rc)) {
|
||||
return rc->recruitcost;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
message *can_recruit(unit *u, const race *rc, order *ord, int now)
|
||||
{
|
||||
region *r = u->region;
|
||||
|
||||
/* this is a very special case because the recruiting unit may be empty
|
||||
* at this point and we have to look at the creating unit instead. This
|
||||
* is done in cansee, which is called indirectly by is_guarded(). */
|
||||
if (is_guarded(r, u)) {
|
||||
return msg_error(u, ord, 70);
|
||||
}
|
||||
|
||||
if (rc == get_race(RC_INSECT)) {
|
||||
gamedate date;
|
||||
get_gamedate(now, &date);
|
||||
if (date.season == SEASON_WINTER && r->terrain != newterrain(T_DESERT)) {
|
||||
bool usepotion = false;
|
||||
unit *u2;
|
||||
|
||||
for (u2 = r->units; u2; u2 = u2->next) {
|
||||
if (fval(u2, UFL_WARMTH)) {
|
||||
usepotion = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!usepotion) {
|
||||
return msg_error(u, ord, 98);
|
||||
}
|
||||
}
|
||||
/* in Gletschern, Eisbergen gar nicht rekrutieren */
|
||||
if (r_insectstalled(r)) {
|
||||
return msg_error(u, ord, 97);
|
||||
}
|
||||
}
|
||||
if (is_cursed(r->attribs, &ct_riotzone)) {
|
||||
/* Die Region befindet sich in Aufruhr */
|
||||
return msg_error(u, ord, 237);
|
||||
}
|
||||
|
||||
if (rc && !playerrace(rc)) {
|
||||
return msg_error(u, ord, 139);
|
||||
}
|
||||
|
||||
if (fval(u, UFL_HERO)) {
|
||||
return msg_feedback(u, ord, "error_herorecruit", "");
|
||||
}
|
||||
if (has_skill(u, SK_MAGIC)) {
|
||||
/* error158;de;{unit} in {region}: '{command}' - Magier arbeiten
|
||||
* grundsaetzlich nur alleine! */
|
||||
return msg_error(u, ord, 158);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void recruit(unit * u, struct order *ord, econ_request ** recruitorders)
|
||||
{
|
||||
region *r = u->region;
|
||||
econ_request *o;
|
||||
int recruitcost = -1;
|
||||
const faction *f = u->faction;
|
||||
const struct race *rc = u_race(u);
|
||||
int n;
|
||||
message *msg;
|
||||
|
||||
init_order_depr(ord);
|
||||
n = getint();
|
||||
if (n <= 0) {
|
||||
syntax_error(u, ord);
|
||||
return;
|
||||
}
|
||||
|
||||
if (u->number == 0) {
|
||||
char token[128];
|
||||
const char *str;
|
||||
|
||||
str = gettoken(token, sizeof(token));
|
||||
if (str && str[0]) {
|
||||
/* Monsters can RECRUIT 15 DRACOID
|
||||
* also: secondary race */
|
||||
rc = findrace(str, f->locale);
|
||||
if (rc != NULL) {
|
||||
recruitcost = recruit_cost(f, rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (recruitcost < 0) {
|
||||
rc = u_race(u);
|
||||
recruitcost = recruit_cost(f, rc);
|
||||
if (recruitcost < 0) {
|
||||
recruitcost = INT_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
if (recruitcost > 0) {
|
||||
int pool;
|
||||
plane *pl = getplane(r);
|
||||
|
||||
if (pl && (pl->flags & PFL_NORECRUITS)) {
|
||||
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_pflnorecruit", ""));
|
||||
return;
|
||||
}
|
||||
|
||||
pool = get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT, recruitcost * n);
|
||||
if (pool < recruitcost) {
|
||||
cmistake(u, ord, 142, MSG_EVENT);
|
||||
return;
|
||||
}
|
||||
pool /= recruitcost;
|
||||
if (n > pool) n = pool;
|
||||
}
|
||||
|
||||
if (!n) {
|
||||
cmistake(u, ord, 142, MSG_EVENT);
|
||||
return;
|
||||
}
|
||||
if (has_skill(u, SK_ALCHEMY)) {
|
||||
if (faction_count_skill(u->faction, SK_ALCHEMY) + n > faction_skill_limit(u->faction, SK_ALCHEMY)) {
|
||||
cmistake(u, ord, 156, MSG_EVENT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
assert(rc);
|
||||
msg = can_recruit(u, rc, ord, turn);
|
||||
if (msg) {
|
||||
add_message(&u->faction->msgs, msg);
|
||||
msg_release(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
u_setrace(u, rc);
|
||||
u->wants = n;
|
||||
o = (econ_request *)calloc(1, sizeof(econ_request));
|
||||
if (!o) abort();
|
||||
o->qty = n;
|
||||
o->unit = u;
|
||||
o->type.recruit.ord = ord;
|
||||
addlist(recruitorders, o);
|
||||
}
|
||||
|
||||
static void friendly_takeover(region * r, faction * f)
|
||||
{
|
||||
region_set_owner(r, f, turn);
|
||||
|
@ -761,7 +390,6 @@ void maintain_buildings(region * r)
|
|||
void economics(region * r)
|
||||
{
|
||||
unit *u;
|
||||
econ_request *recruitorders = NULL;
|
||||
|
||||
/* Geben vor Selbstmord (doquit)! Hier alle unmittelbaren Befehle.
|
||||
* Rekrutieren vor allen Einnahmequellen. Bewachen JA vor Steuern
|
||||
|
@ -784,28 +412,10 @@ void economics(region * r)
|
|||
}
|
||||
}
|
||||
}
|
||||
/* RECRUIT orders */
|
||||
|
||||
if (rules_recruit < 0)
|
||||
recruit_init();
|
||||
for (u = r->units; u; u = u->next) {
|
||||
order *ord;
|
||||
|
||||
if ((rules_recruit & RECRUIT_MERGE) || u->number == 0) {
|
||||
for (ord = u->orders; ord; ord = ord->next) {
|
||||
if (getkeyword(ord) == K_RECRUIT) {
|
||||
recruit(u, ord, &recruitorders);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (recruitorders) {
|
||||
expandrecruit(r, recruitorders);
|
||||
}
|
||||
remove_empty_units_in_region(r);
|
||||
}
|
||||
|
||||
void destroy(region *r) {
|
||||
unit *u;
|
||||
for (u = r->units; u; u = u->next) {
|
||||
order *ord = u->thisorder;
|
||||
keyword_t kwd = getkeyword(ord);
|
||||
|
@ -1490,7 +1100,7 @@ static void expandbuying(region * r, econ_request * buyorders)
|
|||
unsigned int j;
|
||||
for (j = 0; j != norders; j++) {
|
||||
int price, multi;
|
||||
ltype = g_requests[j]->type.trade.ltype;
|
||||
ltype = g_requests[j]->data.trade.ltype;
|
||||
trade = trades;
|
||||
while (trade->type && trade->type != ltype)
|
||||
++trade;
|
||||
|
@ -1671,10 +1281,11 @@ static void buy(unit * u, econ_request ** buyorders, struct order *ord)
|
|||
}
|
||||
o = (econ_request *)calloc(1, sizeof(econ_request));
|
||||
if (!o) abort();
|
||||
o->type.trade.ltype = ltype; /* sollte immer gleich sein */
|
||||
o->data.trade.ltype = ltype; /* sollte immer gleich sein */
|
||||
|
||||
o->unit = u;
|
||||
o->qty = n;
|
||||
o->type = ECON_BUY;
|
||||
addlist(buyorders, o);
|
||||
}
|
||||
|
||||
|
@ -1774,7 +1385,7 @@ static void expandselling(region * r, econ_request * sellorders, int limit)
|
|||
int j;
|
||||
for (j = 0; j != norders; j++) {
|
||||
const luxury_type *search = NULL;
|
||||
const luxury_type *ltype = g_requests[j]->type.trade.ltype;
|
||||
const luxury_type *ltype = g_requests[j]->data.trade.ltype;
|
||||
int multi = r_demand(r, ltype);
|
||||
int i, price;
|
||||
int use = 0;
|
||||
|
@ -1981,7 +1592,7 @@ static bool sell(unit * u, econ_request ** sellorders, struct order *ord)
|
|||
/* Wenn andere Einheiten das selbe verkaufen, muss ihr Zeug abgezogen
|
||||
* werden damit es nicht zweimal verkauft wird: */
|
||||
for (o = *sellorders; o; o = o->next) {
|
||||
if (o->type.trade.ltype == ltype && o->unit->faction == u->faction) {
|
||||
if (o->data.trade.ltype == ltype && o->unit->faction == u->faction) {
|
||||
int fpool =
|
||||
o->qty - get_pooled(o->unit, itype->rtype, GET_RESERVE, INT_MAX);
|
||||
if (fpool < 0) fpool = 0;
|
||||
|
@ -2022,7 +1633,8 @@ static bool sell(unit * u, econ_request ** sellorders, struct order *ord)
|
|||
if (!o) abort();
|
||||
o->unit = u;
|
||||
o->qty = n;
|
||||
o->type.trade.ltype = ltype;
|
||||
o->type = ECON_SELL;
|
||||
o->data.trade.ltype = ltype;
|
||||
addlist(sellorders, o);
|
||||
|
||||
return unlimited;
|
||||
|
@ -2349,36 +1961,39 @@ static void research_cmd(unit * u, struct order *ord)
|
|||
}
|
||||
}
|
||||
|
||||
static void expandentertainment(region * r)
|
||||
static void expandentertainment(region * r, econ_request *ecbegin, econ_request *ecend, long total)
|
||||
{
|
||||
int m = entertainmoney(r);
|
||||
econ_request *o;
|
||||
|
||||
for (o = &entertainers[0]; o != nextentertainer; ++o) {
|
||||
double part = m / (double)entertaining;
|
||||
unit *u = o->unit;
|
||||
for (o = ecbegin; o != ecend; ++o) {
|
||||
if (o->type == ECON_ENTERTAIN) {
|
||||
double part = m / (double)total;
|
||||
unit *u = o->unit;
|
||||
|
||||
if (entertaining <= m)
|
||||
u->n = o->qty;
|
||||
else
|
||||
u->n = (int)(o->qty * part);
|
||||
change_money(u, u->n);
|
||||
rsetmoney(r, rmoney(r) - u->n);
|
||||
m -= u->n;
|
||||
entertaining -= o->qty;
|
||||
if (total <= m)
|
||||
u->n = o->qty;
|
||||
else
|
||||
u->n = (int)(o->qty * part);
|
||||
change_money(u, u->n);
|
||||
rsetmoney(r, rmoney(r) - u->n);
|
||||
m -= u->n;
|
||||
total -= o->qty;
|
||||
|
||||
/* Nur soviel PRODUCEEXP wie auch tatsaechlich gemacht wurde */
|
||||
produceexp(u, SK_ENTERTAINMENT, (u->n < u->number) ? u->n : u->number);
|
||||
add_income(u, IC_ENTERTAIN, o->qty, u->n);
|
||||
fset(u, UFL_LONGACTION | UFL_NOTMOVING);
|
||||
/* Nur soviel PRODUCEEXP wie auch tatsaechlich gemacht wurde */
|
||||
produceexp(u, SK_ENTERTAINMENT, (u->n < u->number) ? u->n : u->number);
|
||||
add_income(u, IC_ENTERTAIN, o->qty, u->n);
|
||||
fset(u, UFL_LONGACTION | UFL_NOTMOVING);
|
||||
}
|
||||
}
|
||||
assert(total == 0);
|
||||
}
|
||||
|
||||
void entertain_cmd(unit * u, struct order *ord)
|
||||
static int entertain_cmd(unit * u, struct order *ord, econ_request **io_req)
|
||||
{
|
||||
region *r = u->region;
|
||||
int max_e;
|
||||
econ_request *o;
|
||||
int wants, max_e;
|
||||
econ_request *req = *io_req;
|
||||
static int entertainbase = 0;
|
||||
static int entertainperlevel = 0;
|
||||
keyword_t kwd;
|
||||
|
@ -2395,39 +2010,38 @@ void entertain_cmd(unit * u, struct order *ord)
|
|||
}
|
||||
if (fval(u, UFL_WERE)) {
|
||||
cmistake(u, ord, 58, MSG_INCOME);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
if (!effskill(u, SK_ENTERTAINMENT, NULL)) {
|
||||
cmistake(u, ord, 58, MSG_INCOME);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
if (u->ship && is_guarded(r, u)) {
|
||||
cmistake(u, ord, 69, MSG_INCOME);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
if (is_cursed(r->attribs, &ct_depression)) {
|
||||
cmistake(u, ord, 28, MSG_INCOME);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u->wants = u->number * (entertainbase + effskill(u, SK_ENTERTAINMENT, NULL)
|
||||
* entertainperlevel);
|
||||
|
||||
max_e = getuint();
|
||||
if (max_e != 0) {
|
||||
if (u->wants > max_e) u->wants = max_e;
|
||||
wants = getuint();
|
||||
max_e = u->number * (entertainbase + effskill(u, SK_ENTERTAINMENT, NULL) * entertainperlevel);
|
||||
if (wants > 0 && wants < max_e) {
|
||||
max_e = wants;
|
||||
}
|
||||
o = nextentertainer++;
|
||||
o->unit = u;
|
||||
o->qty = u->wants;
|
||||
entertaining += o->qty;
|
||||
if (max_e > 0) {
|
||||
add_request(req++, ECON_ENTERTAIN, u, ord, max_e);
|
||||
*io_req = req;
|
||||
}
|
||||
return max_e;
|
||||
}
|
||||
|
||||
/**
|
||||
* \return number of working spaces taken by players
|
||||
*/
|
||||
static void
|
||||
expandwork(region * r, econ_request * work_begin, econ_request * work_end, int maxwork)
|
||||
expandwork(region * r, econ_request * work_begin, econ_request * work_end, int maxwork, long total)
|
||||
{
|
||||
int earnings;
|
||||
/* n: verbleibende Einnahmen */
|
||||
|
@ -2435,37 +2049,41 @@ expandwork(region * r, econ_request * work_begin, econ_request * work_end, int m
|
|||
int jobs = maxwork;
|
||||
int p_wage = wage(r, NULL, NULL, turn);
|
||||
int money = rmoney(r);
|
||||
econ_request *o;
|
||||
if (total > 0 && !rule_autowork()) {
|
||||
econ_request *o;
|
||||
|
||||
for (o = work_begin; o != work_end; ++o) {
|
||||
unit *u = o->unit;
|
||||
int workers;
|
||||
for (o = work_begin; o != work_end; ++o) {
|
||||
if (o->type == ECON_WORK) {
|
||||
unit *u = o->unit;
|
||||
int workers;
|
||||
|
||||
if (u->number == 0)
|
||||
continue;
|
||||
if (u->number == 0)
|
||||
continue;
|
||||
|
||||
if (jobs >= working)
|
||||
workers = u->number;
|
||||
else {
|
||||
int req = (u->number * jobs) % working;
|
||||
workers = u->number * jobs / working;
|
||||
if (req > 0 && rng_int() % working < req)
|
||||
workers++;
|
||||
if (jobs >= total)
|
||||
workers = u->number;
|
||||
else {
|
||||
int req = (u->number * jobs) % total;
|
||||
workers = u->number * jobs / total;
|
||||
if (req > 0 && rng_int() % total < req)
|
||||
workers++;
|
||||
}
|
||||
|
||||
assert(workers >= 0);
|
||||
|
||||
u->n = workers * wage(u->region, u->faction, u_race(u), turn);
|
||||
|
||||
jobs -= workers;
|
||||
assert(jobs >= 0);
|
||||
|
||||
change_money(u, u->n);
|
||||
total -= o->unit->number;
|
||||
add_income(u, IC_WORK, o->qty, u->n);
|
||||
fset(u, UFL_LONGACTION | UFL_NOTMOVING);
|
||||
}
|
||||
}
|
||||
|
||||
assert(workers >= 0);
|
||||
|
||||
u->n = workers * wage(u->region, u->faction, u_race(u), turn);
|
||||
|
||||
jobs -= workers;
|
||||
assert(jobs >= 0);
|
||||
|
||||
change_money(u, u->n);
|
||||
working -= o->unit->number;
|
||||
add_income(u, IC_WORK, o->qty, u->n);
|
||||
fset(u, UFL_LONGACTION | UFL_NOTMOVING);
|
||||
assert(total == 0);
|
||||
}
|
||||
|
||||
if (jobs > rpeasants(r)) {
|
||||
jobs = rpeasants(r);
|
||||
}
|
||||
|
@ -2478,34 +2096,35 @@ expandwork(region * r, econ_request * work_begin, econ_request * work_end, int m
|
|||
rsetmoney(r, money + earnings);
|
||||
}
|
||||
|
||||
static int do_work(unit * u, order * ord, econ_request * o)
|
||||
static int work_cmd(unit * u, order * ord, econ_request ** io_req)
|
||||
{
|
||||
if (playerrace(u_race(u))) {
|
||||
econ_request *req = *io_req;
|
||||
region *r = u->region;
|
||||
int w;
|
||||
|
||||
if (fval(u, UFL_WERE)) {
|
||||
if (ord)
|
||||
if (ord) {
|
||||
cmistake(u, ord, 313, MSG_INCOME);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (u->ship && is_guarded(r, u)) {
|
||||
if (ord)
|
||||
if (ord) {
|
||||
cmistake(u, ord, 69, MSG_INCOME);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
w = wage(r, u->faction, u_race(u), turn);
|
||||
u->wants = u->number * w;
|
||||
o->unit = u;
|
||||
o->qty = u->number * w;
|
||||
working += u->number;
|
||||
return 0;
|
||||
add_request(req++, ECON_WORK, u, ord, w * u->number);
|
||||
*io_req = req;
|
||||
return u->number;
|
||||
}
|
||||
else if (ord && !is_monsters(u->faction)) {
|
||||
ADDMSG(&u->faction->msgs,
|
||||
msg_feedback(u, ord, "race_cantwork", "race", u_race(u)));
|
||||
}
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void expandloot(region * r, econ_request * lootorders)
|
||||
|
@ -2637,6 +2256,7 @@ void tax_cmd(unit * u, struct order *ord, econ_request ** taxorders)
|
|||
o = (econ_request *)calloc(1, sizeof(econ_request));
|
||||
if (!o) abort();
|
||||
o->qty = u->wants / TAXFRACTION;
|
||||
o->type = ECON_TAX;
|
||||
o->unit = u;
|
||||
addlist(taxorders, o);
|
||||
return;
|
||||
|
@ -2703,29 +2323,30 @@ void loot_cmd(unit * u, struct order *ord, econ_request ** lootorders)
|
|||
o = (econ_request *)calloc(1, sizeof(econ_request));
|
||||
if (!o) abort();
|
||||
o->qty = u->wants / TAXFRACTION;
|
||||
o->type = ECON_LOOT;
|
||||
o->unit = u;
|
||||
addlist(lootorders, o);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#define MAX_WORKERS 512
|
||||
void auto_work(region * r)
|
||||
{
|
||||
econ_request workers[MAX_WORKERS];
|
||||
econ_request *nextworker = workers;
|
||||
econ_request *nextrequest = econ_requests;
|
||||
unit *u;
|
||||
long total = 0;
|
||||
|
||||
for (u = r->units; u; u = u->next) {
|
||||
if (!(u->flags & UFL_LONGACTION) && !is_monsters(u->faction)) {
|
||||
if (do_work(u, NULL, nextworker) == 0) {
|
||||
assert(nextworker - workers < MAX_WORKERS);
|
||||
++nextworker;
|
||||
int work = work_cmd(u, NULL, &nextrequest);
|
||||
if (work) {
|
||||
total += work;
|
||||
assert(nextrequest - econ_requests <= MAX_REQUESTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nextworker != workers) {
|
||||
expandwork(r, workers, nextworker, region_maxworkers(r));
|
||||
if (nextrequest != econ_requests) {
|
||||
expandwork(r, econ_requests, nextrequest, region_maxworkers(r), total);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2770,22 +2391,13 @@ static void peasant_taxes(region * r)
|
|||
}
|
||||
}
|
||||
|
||||
static bool rule_auto_taxation(void)
|
||||
{
|
||||
return config_get_int("rules.economy.taxation", 0) != 0;
|
||||
}
|
||||
|
||||
static bool rule_autowork(void) {
|
||||
return config_get_int("work.auto", 0) != 0;
|
||||
}
|
||||
|
||||
void produce(struct region *r)
|
||||
{
|
||||
econ_request workers[MAX_WORKERS];
|
||||
econ_request *taxorders, *lootorders, *sellorders, *stealorders, *buyorders;
|
||||
unit *u;
|
||||
bool limited = true;
|
||||
econ_request *nextworker = workers;
|
||||
long entertaining = 0, working = 0;
|
||||
econ_request *nextrequest = econ_requests;
|
||||
static int bt_cache;
|
||||
static const struct building_type *caravan_bt;
|
||||
static int rc_cache;
|
||||
|
@ -2820,9 +2432,6 @@ void produce(struct region *r)
|
|||
|
||||
buyorders = 0;
|
||||
sellorders = 0;
|
||||
working = 0;
|
||||
nextentertainer = &entertainers[0];
|
||||
entertaining = 0;
|
||||
taxorders = 0;
|
||||
lootorders = 0;
|
||||
stealorders = 0;
|
||||
|
@ -2879,13 +2488,17 @@ void produce(struct region *r)
|
|||
|
||||
switch (todo) {
|
||||
case K_ENTERTAIN:
|
||||
entertain_cmd(u, u->thisorder);
|
||||
entertaining += entertain_cmd(u, u->thisorder, &nextrequest);
|
||||
assert(nextrequest - econ_requests <= MAX_REQUESTS);
|
||||
break;
|
||||
|
||||
case K_WORK:
|
||||
if (!rule_autowork() && do_work(u, u->thisorder, nextworker) == 0) {
|
||||
assert(nextworker - workers < MAX_WORKERS);
|
||||
++nextworker;
|
||||
if (!rule_autowork()) {
|
||||
int work = work_cmd(u, u->thisorder, &nextrequest);
|
||||
if (work != 0) {
|
||||
working += work;
|
||||
assert(nextrequest - econ_requests <= MAX_REQUESTS);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -2927,11 +2540,11 @@ void produce(struct region *r)
|
|||
* Befehlen, die den Bauern mehr Geld geben, damit man aus den Zahlen der
|
||||
* letzten Runde berechnen kann, wieviel die Bauern fuer Unterhaltung
|
||||
* auszugeben bereit sind. */
|
||||
if (entertaining)
|
||||
expandentertainment(r);
|
||||
if (!rule_autowork()) {
|
||||
expandwork(r, workers, nextworker, region_maxworkers(r));
|
||||
if (entertaining > 0) {
|
||||
expandentertainment(r, econ_requests, nextrequest, entertaining);
|
||||
}
|
||||
expandwork(r, econ_requests, nextrequest, region_maxworkers(r), working);
|
||||
|
||||
if (taxorders) {
|
||||
expandtax(r, taxorders);
|
||||
free_requests(taxorders);
|
||||
|
|
|
@ -55,10 +55,17 @@ extern "C" {
|
|||
struct econ_request *next;
|
||||
struct unit *unit;
|
||||
int qty;
|
||||
enum econ_type {
|
||||
ECON_LIST,
|
||||
ECON_ENTERTAIN,
|
||||
ECON_WORK,
|
||||
ECON_TAX,
|
||||
ECON_LOOT,
|
||||
ECON_BUY,
|
||||
ECON_SELL,
|
||||
ECON_STEAL,
|
||||
} type;
|
||||
union {
|
||||
struct {
|
||||
struct order *ord;
|
||||
} recruit;
|
||||
struct {
|
||||
int no;
|
||||
bool goblin; /* stealing */
|
||||
|
@ -66,18 +73,19 @@ extern "C" {
|
|||
struct {
|
||||
const struct luxury_type *ltype; /* trading */
|
||||
} trade;
|
||||
} type;
|
||||
} data;
|
||||
} econ_request;
|
||||
|
||||
int expand_production(struct region * r, struct econ_request * requests, struct econ_request ***results);
|
||||
|
||||
int income(const struct unit *u);
|
||||
int entertainmoney(const struct region *r);
|
||||
|
||||
void economics(struct region *r);
|
||||
void destroy(struct region *r);
|
||||
void produce(struct region *r);
|
||||
void auto_work(struct region *r);
|
||||
|
||||
int expand_production(struct region * r, struct econ_request * requests, struct econ_request ***results);
|
||||
|
||||
typedef enum income_t { IC_WORK, IC_ENTERTAIN, IC_TAX, IC_TRADE, IC_TRADETAX, IC_STEAL, IC_MAGIC, IC_LOOT } income_t;
|
||||
void add_income(struct unit * u, income_t type, int want, int qty);
|
||||
|
||||
|
@ -95,9 +103,6 @@ extern "C" {
|
|||
void steal_cmd(struct unit * u, struct order *ord, struct econ_request ** stealorders);
|
||||
void expandstealing(struct region * r, struct econ_request * stealorders);
|
||||
|
||||
struct message *can_recruit(struct unit *u, const struct race *rc, struct order *ord, int now);
|
||||
void add_recruits(struct unit * u, int number, int wanted);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#endif
|
||||
#include <kernel/config.h>
|
||||
#include "economy.h"
|
||||
#include "recruit.h"
|
||||
|
||||
#include <util/message.h>
|
||||
#include <kernel/building.h>
|
||||
|
@ -162,7 +163,7 @@ static void test_heroes_dont_recruit(CuTest * tc) {
|
|||
fset(u, UFL_HERO);
|
||||
unit_addorder(u, create_order(K_RECRUIT, default_locale, "1"));
|
||||
|
||||
economics(u->region);
|
||||
recruit(u->region);
|
||||
|
||||
CuAssertIntEquals(tc, 1, u->number);
|
||||
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error_herorecruit"));
|
||||
|
@ -178,7 +179,7 @@ static void test_normals_recruit(CuTest * tc) {
|
|||
u = create_recruiter();
|
||||
unit_addorder(u, create_order(K_RECRUIT, default_locale, "1"));
|
||||
|
||||
economics(u->region);
|
||||
recruit(u->region);
|
||||
|
||||
CuAssertIntEquals(tc, 2, u->number);
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015,
|
||||
Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
@ -216,6 +216,7 @@ faction *addfaction(const char *email, const char *password,
|
|||
const struct race * frace, const struct locale * loc)
|
||||
{
|
||||
faction *f = calloc(1, sizeof(faction));
|
||||
const char *fname;
|
||||
char buf[128];
|
||||
|
||||
if (!f) abort();
|
||||
|
@ -255,7 +256,8 @@ faction *addfaction(const char *email, const char *password,
|
|||
addlist(&factions, f);
|
||||
fhash(f);
|
||||
|
||||
slprintf(buf, sizeof(buf), "%s %s", LOC(loc, "factiondefault"), itoa36(f->no));
|
||||
fname = LOC(loc, "factiondefault");
|
||||
slprintf(buf, sizeof(buf), "%s %s", fname ? fname : "faction", itoa36(f->no));
|
||||
f->name = str_strdup(buf);
|
||||
|
||||
if (!f->race) {
|
||||
|
@ -300,7 +302,7 @@ unit *addplayer(region * r, faction * f)
|
|||
} while (rc == NULL || urc == RC_DAEMON || !playerrace(rc));
|
||||
u->irace = rc;
|
||||
}
|
||||
f->lastorders = 0;
|
||||
f->lastorders = turn;
|
||||
return u;
|
||||
}
|
||||
|
||||
|
|
|
@ -344,6 +344,7 @@ static void test_addplayer(CuTest *tc) {
|
|||
CuAssertPtrNotNull(tc, u);
|
||||
CuAssertPtrEquals(tc, r, u->region);
|
||||
CuAssertPtrEquals(tc, f, u->faction);
|
||||
CuAssertIntEquals(tc, turn, u->faction->lastorders);
|
||||
CuAssertIntEquals(tc, i_get(u->items, itype), 10);
|
||||
CuAssertPtrNotNull(tc, u->orders);
|
||||
CuAssertIntEquals(tc, K_WORK, getkeyword(u->orders));
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
@ -625,7 +625,11 @@ void rsetpeasants(region * r, int value)
|
|||
assert(r->land || value==0);
|
||||
assert(value >= 0);
|
||||
if (r->land) {
|
||||
r->land->peasants = value;
|
||||
if (value > USHRT_MAX) {
|
||||
log_warning("region %s cannot have %d peasants.", regionname(r, NULL), value);
|
||||
value = USHRT_MAX;
|
||||
}
|
||||
r->land->peasants = (unsigned short)value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -639,7 +643,11 @@ void rsethorses(const region * r, int value)
|
|||
assert(r->land || value==0);
|
||||
assert(value >= 0);
|
||||
if (r->land) {
|
||||
r->land->horses = value;
|
||||
if (value > USHRT_MAX) {
|
||||
log_warning("region %s cannot have %d horses.", regionname(r, NULL), value);
|
||||
value = USHRT_MAX;
|
||||
}
|
||||
r->land->horses = (unsigned short)value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -659,15 +667,18 @@ void rsetmoney(region * r, int value)
|
|||
|
||||
int rherbs(const region *r)
|
||||
{
|
||||
return r->land?r->land->herbs:0;
|
||||
return r->land ? r->land->herbs : 0;
|
||||
}
|
||||
|
||||
void rsetherbs(region *r, int value)
|
||||
{
|
||||
assert(r->land || value==0);
|
||||
assert(value >= 0 && value<=SHRT_MAX);
|
||||
if (r->land) {
|
||||
r->land->herbs = value;
|
||||
if (value > USHRT_MAX) {
|
||||
log_warning("region %s cannot have %d herbs.", regionname(r, NULL), value);
|
||||
value = USHRT_MAX;
|
||||
}
|
||||
r->land->herbs = (unsigned short)value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1478,7 +1489,7 @@ const char *region_getname(const region * r)
|
|||
int region_get_morale(const region * r)
|
||||
{
|
||||
if (r->land) {
|
||||
assert(r->land->morale >= 0 && r->land->morale <= MORALE_MAX);
|
||||
assert(r->land->morale <= MORALE_MAX);
|
||||
return r->land->morale;
|
||||
}
|
||||
return -1;
|
||||
|
@ -1487,11 +1498,11 @@ int region_get_morale(const region * r)
|
|||
void region_set_morale(region * r, int morale, int turn)
|
||||
{
|
||||
if (r->land) {
|
||||
r->land->morale = morale;
|
||||
r->land->morale = (unsigned short)morale;
|
||||
if (turn >= 0 && r->land->ownership) {
|
||||
r->land->ownership->morale_turn = turn;
|
||||
}
|
||||
assert(r->land->morale >= 0 && r->land->morale <= MORALE_MAX);
|
||||
assert(r->land->morale <= MORALE_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -97,12 +97,12 @@ extern "C" {
|
|||
char *display;
|
||||
demand *demands;
|
||||
const struct item_type *herbtype;
|
||||
int herbs;
|
||||
int morale;
|
||||
unsigned short horses;
|
||||
unsigned short herbs;
|
||||
unsigned short peasants;
|
||||
unsigned short morale;
|
||||
short newpeasants;
|
||||
int trees[3]; /* 0 -> seeds, 1 -> shoots, 2 -> trees */
|
||||
int horses;
|
||||
int peasants;
|
||||
int newpeasants;
|
||||
int money;
|
||||
struct region_owner *ownership;
|
||||
} land_region;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
@ -318,7 +318,10 @@ int crew_skill(const ship *sh) {
|
|||
|
||||
for (u = sh->region->units; u; u = u->next) {
|
||||
if (u->ship == sh) {
|
||||
n += effskill(u, SK_SAILING, NULL) * u->number;
|
||||
int es = effskill(u, SK_SAILING, NULL);
|
||||
if (es >= sh->type->minskill) {
|
||||
n += es * u->number;
|
||||
}
|
||||
}
|
||||
}
|
||||
return n;
|
||||
|
|
|
@ -384,27 +384,6 @@ static void test_stype_defaults(CuTest *tc) {
|
|||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_crew_skill(CuTest *tc) {
|
||||
ship *sh;
|
||||
region *r;
|
||||
struct faction *f;
|
||||
int i;
|
||||
|
||||
test_setup();
|
||||
test_create_world();
|
||||
r = test_create_region(0, 0, NULL);
|
||||
f = test_create_faction(NULL);
|
||||
assert(r && f);
|
||||
sh = test_create_ship(r, st_find("boat"));
|
||||
for (i = 0; i != 4; ++i) {
|
||||
unit * u = test_create_unit(f, r);
|
||||
set_level(u, SK_SAILING, 5);
|
||||
u->ship = sh;
|
||||
}
|
||||
CuAssertIntEquals(tc, 20, crew_skill(sh));
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
static ship *setup_ship(void) {
|
||||
region *r;
|
||||
ship_type *stype;
|
||||
|
@ -651,6 +630,35 @@ static void test_shipspeed_max_range(CuTest *tc) {
|
|||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_crew_skill(CuTest *tc) {
|
||||
ship_type *stype;
|
||||
ship * sh;
|
||||
unit *u;
|
||||
region *r;
|
||||
|
||||
test_setup();
|
||||
stype = test_create_shiptype("kayak");
|
||||
CuAssertIntEquals(tc, 1, stype->minskill);
|
||||
r = test_create_ocean(0, 0);
|
||||
sh = test_create_ship(r, stype);
|
||||
CuAssertIntEquals(tc, 0, crew_skill(sh));
|
||||
u = test_create_unit(test_create_faction(NULL), r);
|
||||
set_level(u, SK_SAILING, 1);
|
||||
CuAssertIntEquals(tc, 0, crew_skill(sh));
|
||||
u_set_ship(u, sh);
|
||||
set_level(u, SK_SAILING, 1);
|
||||
CuAssertIntEquals(tc, 1, crew_skill(sh));
|
||||
set_number(u, 10);
|
||||
CuAssertIntEquals(tc, 10, crew_skill(sh));
|
||||
stype->minskill = 2;
|
||||
CuAssertIntEquals(tc, 0, crew_skill(sh));
|
||||
set_level(u, SK_SAILING, 2);
|
||||
CuAssertIntEquals(tc, 20, crew_skill(sh));
|
||||
set_level(u, SK_SAILING, 3);
|
||||
CuAssertIntEquals(tc, 30, crew_skill(sh));
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
CuSuite *get_ship_suite(void)
|
||||
{
|
||||
CuSuite *suite = CuSuiteNew();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -59,6 +59,7 @@ struct weapon_type;
|
|||
typedef enum {
|
||||
seen_none,
|
||||
seen_neighbour,
|
||||
seen_lighthouse_land,
|
||||
seen_lighthouse,
|
||||
seen_travel,
|
||||
seen_unit,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
64
src/laws.c
64
src/laws.c
|
@ -1,6 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2014,
|
||||
Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
@ -36,6 +35,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include "monsters.h"
|
||||
#include "move.h"
|
||||
#include "randenc.h"
|
||||
#include "recruit.h"
|
||||
#include "renumber.h"
|
||||
#include "spy.h"
|
||||
#include "study.h"
|
||||
|
@ -281,14 +281,13 @@ static void live(region * r)
|
|||
#define MAX_EMIGRATION(p) ((p)/MAXDIRECTIONS)
|
||||
#define MAX_IMMIGRATION(p) ((p)*2/3)
|
||||
|
||||
static void calculate_emigration(region * r)
|
||||
void peasant_migration(region * r)
|
||||
{
|
||||
int i;
|
||||
int maxp = region_maxworkers(r);
|
||||
int rp = rpeasants(r);
|
||||
int max_immigrants = MAX_IMMIGRATION(maxp - rp);
|
||||
|
||||
|
||||
if (volcano_module()) {
|
||||
static int terrain_cache;
|
||||
static const terrain_type *t_volcano;
|
||||
|
@ -314,8 +313,14 @@ static void calculate_emigration(region * r)
|
|||
|
||||
if (max_emigration > 0) {
|
||||
if (max_emigration > max_immigrants) max_emigration = max_immigrants;
|
||||
r->land->newpeasants += max_emigration;
|
||||
rc->land->newpeasants -= max_emigration;
|
||||
if (max_emigration + r->land->newpeasants > USHRT_MAX) {
|
||||
max_emigration = USHRT_MAX - r->land->newpeasants;
|
||||
}
|
||||
if (max_emigration + rc->land->newpeasants > USHRT_MAX) {
|
||||
max_emigration = USHRT_MAX - rc->land->newpeasants;
|
||||
}
|
||||
r->land->newpeasants += (short)max_emigration;
|
||||
rc->land->newpeasants -= (short)max_emigration;
|
||||
max_immigrants -= max_emigration;
|
||||
}
|
||||
}
|
||||
|
@ -779,6 +784,7 @@ void immigration(void)
|
|||
/* FIXME: kann ernsthaft abs(newpeasants) > rpeasants(r) sein? */
|
||||
if (rp < 0) rp = 0;
|
||||
rsetpeasants(r, rp);
|
||||
r->land->newpeasants = 0;
|
||||
}
|
||||
/* Genereate some (0-6 depending on the income) peasants out of nothing */
|
||||
/* if less than 50 are in the region and there is space and no monster or demon units in the region */
|
||||
|
@ -878,7 +884,7 @@ void demographics(void)
|
|||
/* Seuchen erst nachdem die Bauern sich vermehrt haben
|
||||
* und gewandert sind */
|
||||
|
||||
calculate_emigration(r);
|
||||
peasant_migration(r);
|
||||
peasants(r, peasant_rules);
|
||||
|
||||
if (r->age > 20) {
|
||||
|
@ -947,6 +953,8 @@ void transfer_faction(faction *fsrc, faction *fdst) {
|
|||
int skill_count[MAXSKILLS];
|
||||
int skill_limit[MAXSKILLS];
|
||||
|
||||
assert(fsrc != fdst);
|
||||
|
||||
for (sk = 0; sk != MAXSKILLS; ++sk) {
|
||||
skill_limit[sk] = faction_skill_limit(fdst, sk);
|
||||
}
|
||||
|
@ -963,7 +971,10 @@ void transfer_faction(faction *fsrc, faction *fdst) {
|
|||
}
|
||||
}
|
||||
|
||||
for (u = fsrc->units; u != NULL; u = u->nextF) {
|
||||
u = fsrc->units;
|
||||
while (u) {
|
||||
unit *unext = u->nextF;
|
||||
|
||||
if (u_race(u) == fdst->race) {
|
||||
u->flags &= ~UFL_HERO;
|
||||
if (give_unit_allowed(u) == 0) {
|
||||
|
@ -978,12 +989,15 @@ void transfer_faction(faction *fsrc, faction *fdst) {
|
|||
}
|
||||
}
|
||||
if (i != u->skill_size) {
|
||||
u = u->nextF;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ADDMSG(&fdst->msgs, msg_message("transfer_unit", "unit", u));
|
||||
u_setfaction(u, fdst);
|
||||
}
|
||||
}
|
||||
u = unext;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1003,6 +1017,7 @@ int quit_cmd(unit * u, struct order *ord)
|
|||
param_t p;
|
||||
p = getparam(f->locale);
|
||||
if (p == P_FACTION) {
|
||||
#ifdef QUIT_WITH_TRANSFER
|
||||
faction *f2 = getfaction();
|
||||
if (f2 == NULL) {
|
||||
cmistake(u, ord, 66, MSG_EVENT);
|
||||
|
@ -1015,17 +1030,23 @@ int quit_cmd(unit * u, struct order *ord)
|
|||
else {
|
||||
unit *u2;
|
||||
for (u2 = u->region->units; u2; u2 = u2->next) {
|
||||
if (u2->faction == f2 && ucontact(u2, u)) {
|
||||
transfer_faction(u->faction, u2->faction);
|
||||
break;
|
||||
if (u2->faction == f2) {
|
||||
if (ucontact(u2, u)) {
|
||||
transfer_faction(u->faction, u2->faction);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (u2 == NULL) {
|
||||
/* no target unit found */
|
||||
cmistake(u, ord, 0, MSG_EVENT);
|
||||
cmistake(u, ord, 40, MSG_EVENT);
|
||||
flags = 0;
|
||||
}
|
||||
}
|
||||
#else
|
||||
log_error("faction %s: QUIT FACTION is disabled.", factionname(f));
|
||||
flags = 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
f->flags |= flags;
|
||||
|
@ -2152,8 +2173,13 @@ int banner_cmd(unit * u, struct order *ord)
|
|||
|
||||
init_order_depr(ord);
|
||||
s = getstrtoken();
|
||||
faction_setbanner(u->faction, s);
|
||||
ADDMSG(&u->faction->msgs, msg_message("changebanner", "value", s));
|
||||
if (!s || !s[0]) {
|
||||
cmistake(u, ord, 125, MSG_EVENT);
|
||||
}
|
||||
else {
|
||||
faction_setbanner(u->faction, s);
|
||||
ADDMSG(&u->faction->msgs, msg_message("changebanner", "value", s));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -3872,7 +3898,6 @@ void init_processor(void)
|
|||
add_proc_order(p, K_GROUP, group_cmd, 0, NULL);
|
||||
|
||||
p += 10;
|
||||
add_proc_order(p, K_QUIT, quit_cmd, 0, NULL);
|
||||
add_proc_order(p, K_URSPRUNG, origin_cmd, 0, NULL);
|
||||
add_proc_order(p, K_ALLY, ally_cmd, 0, NULL);
|
||||
add_proc_order(p, K_PREFIX, prefix_cmd, 0, NULL);
|
||||
|
@ -3896,6 +3921,7 @@ void init_processor(void)
|
|||
p += 10; /* all claims must be done before we can USE */
|
||||
add_proc_region(p, enter_1, "Betreten (1. Versuch)"); /* for GIVE CONTROL */
|
||||
add_proc_order(p, K_USE, use_cmd, 0, "Benutzen");
|
||||
add_proc_order(p, K_QUIT, quit_cmd, 0, "Stirb");
|
||||
|
||||
p += 10; /* in case it has any effects on alliance victories */
|
||||
add_proc_order(p, K_GIVE, give_control_cmd, 0, "GIB KOMMANDO");
|
||||
|
@ -3923,11 +3949,14 @@ void init_processor(void)
|
|||
if (rule_force_leave(FORCE_LEAVE_ALL)) {
|
||||
add_proc_region(p, do_force_leave, "kick non-allies out of buildings/ships");
|
||||
}
|
||||
add_proc_region(p, economics, "Zerstoeren, Geben, Rekrutieren, Vergessen");
|
||||
add_proc_region(p, economics, "Geben, Vergessen");
|
||||
add_proc_region(p+1, recruit, "Rekrutieren");
|
||||
add_proc_region(p+2, destroy, "Zerstoeren");
|
||||
|
||||
/* all recruitment must be finished before we can calculate
|
||||
* promotion cost of ability */
|
||||
p += 10;
|
||||
add_proc_global(p, quit, "Sterben");
|
||||
add_proc_order(p, K_PROMOTION, promotion_cmd, 0, "Heldenbefoerderung");
|
||||
|
||||
p += 10;
|
||||
|
@ -3936,9 +3965,6 @@ void init_processor(void)
|
|||
}
|
||||
add_proc_postregion(p, maintain_buildings, "Gebaeudeunterhalt");
|
||||
|
||||
p += 10; /* QUIT fuer sich alleine */
|
||||
add_proc_global(p, quit, "Sterben");
|
||||
|
||||
if (!keyword_disabled(K_CAST)) {
|
||||
p += 10;
|
||||
add_proc_global(p, magic, "Zaubern");
|
||||
|
|
|
@ -93,6 +93,8 @@ extern "C" {
|
|||
int reserve_cmd(struct unit *u, struct order *ord);
|
||||
int reserve_self(struct unit *u, struct order *ord);
|
||||
int claim_cmd(struct unit *u, struct order *ord);
|
||||
void transfer_faction(struct faction *fsrc, struct faction *fdst);
|
||||
void peasant_migration(struct region * r);
|
||||
|
||||
void nmr_warnings(void);
|
||||
bool nmr_death(const struct faction * f, int turn, int timeout);
|
||||
|
@ -119,6 +121,8 @@ extern "C" {
|
|||
|
||||
enum param_t findparam_ex(const char *s, const struct locale * lang);
|
||||
|
||||
#define QUIT_WITH_TRANSFER
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
140
src/laws.test.c
140
src/laws.test.c
|
@ -1248,6 +1248,68 @@ static void test_ally_cmd_errors(CuTest *tc) {
|
|||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_banner_cmd(CuTest *tc) {
|
||||
unit *u;
|
||||
faction *f;
|
||||
order *ord;
|
||||
|
||||
test_setup();
|
||||
mt_create_error(125);
|
||||
mt_create_va(mt_new("changebanner", NULL), "value:string", MT_NEW_END);
|
||||
u = test_create_unit(f = test_create_faction(NULL), test_create_region(0, 0, NULL));
|
||||
|
||||
ord = create_order(K_BANNER, f->locale, "Hodor!");
|
||||
banner_cmd(u, ord);
|
||||
CuAssertStrEquals(tc, "Hodor!", faction_getbanner(f));
|
||||
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "changebanner"));
|
||||
free_order(ord);
|
||||
test_clear_messages(f);
|
||||
|
||||
ord = create_order(K_BANNER, f->locale, NULL);
|
||||
banner_cmd(u, ord);
|
||||
CuAssertStrEquals(tc, "Hodor!", faction_getbanner(f));
|
||||
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error125"));
|
||||
free_order(ord);
|
||||
test_clear_messages(f);
|
||||
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_email_cmd(CuTest *tc) {
|
||||
unit *u;
|
||||
faction *f;
|
||||
order *ord;
|
||||
|
||||
test_setup();
|
||||
mt_create_error(85);
|
||||
mt_create_va(mt_new("changemail", NULL), "value:string", MT_NEW_END);
|
||||
mt_create_va(mt_new("changemail_invalid", NULL), "value:string", MT_NEW_END);
|
||||
u = test_create_unit(f = test_create_faction(NULL), test_create_region(0, 0, NULL));
|
||||
|
||||
ord = create_order(K_EMAIL, f->locale, "hodor@example.com");
|
||||
email_cmd(u, ord);
|
||||
CuAssertStrEquals(tc, "hodor@example.com", f->email);
|
||||
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "changemail"));
|
||||
free_order(ord);
|
||||
test_clear_messages(f);
|
||||
|
||||
ord = create_order(K_EMAIL, f->locale, "example.com");
|
||||
email_cmd(u, ord);
|
||||
CuAssertStrEquals(tc, "hodor@example.com", f->email);
|
||||
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "changemail_invalid"));
|
||||
free_order(ord);
|
||||
test_clear_messages(f);
|
||||
|
||||
ord = create_order(K_EMAIL, f->locale, NULL);
|
||||
email_cmd(u, ord);
|
||||
CuAssertStrEquals(tc, "hodor@example.com", f->email);
|
||||
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error85"));
|
||||
free_order(ord);
|
||||
test_clear_messages(f);
|
||||
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_name_cmd(CuTest *tc) {
|
||||
unit *u;
|
||||
faction *f;
|
||||
|
@ -1869,6 +1931,53 @@ static void test_long_order_on_ocean(CuTest *tc) {
|
|||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_peasant_migration(CuTest *tc) {
|
||||
region *r1, *r2;
|
||||
int rmax;
|
||||
|
||||
test_setup();
|
||||
config_set("rules.economy.repopulate_maximum", "0");
|
||||
r1 = test_create_plain(0, 0);
|
||||
rsettrees(r1, 0, 0);
|
||||
rsettrees(r1, 1, 0);
|
||||
rsettrees(r1, 2, 0);
|
||||
rmax = region_maxworkers(r1);
|
||||
r2 = test_create_plain(0, 1);
|
||||
rsettrees(r2, 0, 0);
|
||||
rsettrees(r2, 1, 0);
|
||||
rsettrees(r2, 2, 0);
|
||||
|
||||
rsetpeasants(r1, rmax - 90);
|
||||
rsetpeasants(r2, rmax);
|
||||
peasant_migration(r1);
|
||||
immigration();
|
||||
CuAssertIntEquals(tc, rmax - 90, rpeasants(r1));
|
||||
CuAssertIntEquals(tc, rmax, rpeasants(r2));
|
||||
|
||||
rsetpeasants(r1, rmax - 90);
|
||||
rsetpeasants(r2, rmax + 60);
|
||||
peasant_migration(r1);
|
||||
immigration();
|
||||
CuAssertIntEquals(tc, rmax - 80, rpeasants(r1));
|
||||
CuAssertIntEquals(tc, rmax + 50, rpeasants(r2));
|
||||
|
||||
rsetpeasants(r1, rmax - 6); /* max 4 immigrants. */
|
||||
rsetpeasants(r2, rmax + 60); /* max 10 emigrants. */
|
||||
peasant_migration(r1);
|
||||
immigration(); /* 4 peasants will move */
|
||||
CuAssertIntEquals(tc, rmax - 2, rpeasants(r1));
|
||||
CuAssertIntEquals(tc, rmax + 56, rpeasants(r2));
|
||||
|
||||
rsetpeasants(r1, rmax - 6); /* max 4 immigrants. */
|
||||
rsetpeasants(r2, rmax + 6); /* max 1 emigrant. */
|
||||
peasant_migration(r1);
|
||||
immigration(); /* 4 peasants will move */
|
||||
CuAssertIntEquals(tc, rmax - 5, rpeasants(r1));
|
||||
CuAssertIntEquals(tc, rmax + 5, rpeasants(r2));
|
||||
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_quit(CuTest *tc) {
|
||||
faction *f;
|
||||
unit *u;
|
||||
|
@ -1891,6 +2000,7 @@ static void test_quit(CuTest *tc) {
|
|||
test_teardown();
|
||||
}
|
||||
|
||||
#ifdef QUIT_WITH_TRANSFER
|
||||
/**
|
||||
* Gifting units to another faction upon voluntary death (QUIT).
|
||||
*/
|
||||
|
@ -2012,6 +2122,30 @@ static void test_quit_transfer_hero(CuTest *tc) {
|
|||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_transfer_faction(CuTest *tc) {
|
||||
faction *f1, *f2;
|
||||
unit *u1, *u2, *u3, *u4;
|
||||
region *r;
|
||||
|
||||
test_setup();
|
||||
r = test_create_plain(0, 0);
|
||||
f1 = test_create_faction(NULL);
|
||||
f2 = test_create_faction(NULL);
|
||||
u1 = test_create_unit(f1, r);
|
||||
u2 = test_create_unit(f1, r);
|
||||
u_setrace(u2, test_create_race("smurf"));
|
||||
u3 = test_create_unit(f2, r);
|
||||
u4 = test_create_unit(f1, r);
|
||||
transfer_faction(f1, f2);
|
||||
CuAssertPtrEquals(tc, f2, u1->faction);
|
||||
CuAssertPtrEquals(tc, f1, u2->faction);
|
||||
CuAssertPtrEquals(tc, f2, u3->faction);
|
||||
CuAssertPtrEquals(tc, f2, u4->faction);
|
||||
|
||||
test_teardown();
|
||||
}
|
||||
#endif
|
||||
|
||||
CuSuite *get_laws_suite(void)
|
||||
{
|
||||
CuSuite *suite = CuSuiteNew();
|
||||
|
@ -2021,6 +2155,8 @@ CuSuite *get_laws_suite(void)
|
|||
SUITE_ADD_TEST(suite, test_nmr_warnings);
|
||||
SUITE_ADD_TEST(suite, test_ally_cmd);
|
||||
SUITE_ADD_TEST(suite, test_name_cmd);
|
||||
SUITE_ADD_TEST(suite, test_banner_cmd);
|
||||
SUITE_ADD_TEST(suite, test_email_cmd);
|
||||
SUITE_ADD_TEST(suite, test_name_cmd_2274);
|
||||
SUITE_ADD_TEST(suite, test_name_unit);
|
||||
SUITE_ADD_TEST(suite, test_name_region);
|
||||
|
@ -2088,10 +2224,14 @@ CuSuite *get_laws_suite(void)
|
|||
SUITE_ADD_TEST(suite, test_long_orders);
|
||||
SUITE_ADD_TEST(suite, test_long_order_on_ocean);
|
||||
SUITE_ADD_TEST(suite, test_quit);
|
||||
SUITE_ADD_TEST(suite, test_peasant_migration);
|
||||
#ifdef QUIT_WITH_TRANSFER
|
||||
SUITE_ADD_TEST(suite, test_quit_transfer);
|
||||
SUITE_ADD_TEST(suite, test_quit_transfer_limited);
|
||||
SUITE_ADD_TEST(suite, test_quit_transfer_migrants);
|
||||
SUITE_ADD_TEST(suite, test_quit_transfer_hero);
|
||||
SUITE_ADD_TEST(suite, test_transfer_faction);
|
||||
#endif
|
||||
|
||||
return suite;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2014,
|
||||
Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2018, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
@ -193,7 +193,7 @@ static int parse_args(int argc, char **argv)
|
|||
else if (argi[1] == '-') { /* long format */
|
||||
if (strcmp(argi + 2, "version") == 0) {
|
||||
printf("Eressea version %s, "
|
||||
"Copyright (C) 2018 Enno Rehling et al.\n",
|
||||
"Copyright (C) 2019 Enno Rehling et al.\n",
|
||||
eressea_version());
|
||||
return 1;
|
||||
#ifdef USE_CURSES
|
||||
|
|
|
@ -565,8 +565,8 @@ int autoseed(newfaction ** players, int nsize, int max_agediff)
|
|||
nfp = &nextf->next;
|
||||
while (*nfp) {
|
||||
newfaction *nf = *nfp;
|
||||
if (strcmp(nextf->email, nf->email) == 0) {
|
||||
log_warning("Duplicate email %s\n", nf->email?nf->email:"");
|
||||
if (nf->email && nextf->email && strcmp(nextf->email, nf->email) == 0) {
|
||||
log_warning("Duplicate email %s\n", nf->email ? nf->email : "");
|
||||
*nfp = nf->next;
|
||||
free_newfaction(nf);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Eressea PB(E)M host Copyright (C) 1998-2015
|
||||
* Eressea PB(E)M host Copyright (C) 1998-2019
|
||||
* Christian Schlittchen (corwin@amber.kn-bremen.de)
|
||||
* Katja Zedel (katze@felidae.kn-bremen.de)
|
||||
* Henning Peters (faroul@beyond.kn-bremen.de)
|
||||
|
@ -730,6 +730,17 @@ static order *plan_dragon(unit * u)
|
|||
return long_order;
|
||||
}
|
||||
|
||||
static void monster_cannibalism(unit *u) {
|
||||
unit *u2;
|
||||
|
||||
for (u2 = u->next; u2; u2 = u2->next) {
|
||||
if (u2->_race == u->_race) {
|
||||
stats_count("monsters.cannibalism", u2->number);
|
||||
u2->number = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void plan_monsters(faction * f)
|
||||
{
|
||||
region *r;
|
||||
|
@ -748,14 +759,20 @@ void plan_monsters(faction * f)
|
|||
bool can_move = true;
|
||||
|
||||
/* Ab hier nur noch Befehle fuer NPC-Einheiten. */
|
||||
if (u->faction!=f)
|
||||
if (u->faction != f || u->number <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Parteitarnung von Monstern ist doof: */
|
||||
if (fval(u, UFL_ANON_FACTION)) {
|
||||
u->flags &= ~UFL_ANON_FACTION;
|
||||
}
|
||||
|
||||
if (rc->splitsize < 10) {
|
||||
/* hermit-type monsters eat each other */
|
||||
monster_cannibalism(u);
|
||||
}
|
||||
|
||||
if (skill_enabled(SK_PERCEPTION)) {
|
||||
/* Monster bekommen jede Runde ein paar Tage Wahrnehmung dazu */
|
||||
produceexp(u, SK_PERCEPTION, u->number);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2014,
|
||||
Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2018,
|
||||
Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
/*
|
||||
*
|
||||
*
|
||||
* Eressea PB(E)M host Copyright (C) 1998-2015
|
||||
* Eressea PB(E)M host Copyright (C) 1998-2019
|
||||
* Christian Schlittchen (corwin@amber.kn-bremen.de)
|
||||
* Katja Zedel (katze@felidae.kn-bremen.de)
|
||||
* Henning Peters (faroul@beyond.kn-bremen.de)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Eressea PB(E)M host Copyright (C) 1998-2015
|
||||
* Eressea PB(E)M host Copyright (C) 1998-2019
|
||||
* Christian Schlittchen (corwin@amber.kn-bremen.de)
|
||||
* Katja Zedel (katze@felidae.kn-bremen.de)
|
||||
* Henning Peters (faroul@beyond.kn-bremen.de)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
|
|
|
@ -0,0 +1,506 @@
|
|||
/*
|
||||
Copyright (c) 1998-2019,
|
||||
Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
**/
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <platform.h>
|
||||
#endif
|
||||
|
||||
#include "recruit.h"
|
||||
|
||||
#include "alchemy.h"
|
||||
#include "direction.h"
|
||||
#include "donations.h"
|
||||
#include "guard.h"
|
||||
#include "give.h"
|
||||
#include "laws.h"
|
||||
#include "randenc.h"
|
||||
#include "spy.h"
|
||||
#include "study.h"
|
||||
#include "move.h"
|
||||
#include "monsters.h"
|
||||
#include "morale.h"
|
||||
#include "reports.h"
|
||||
|
||||
#include <attributes/reduceproduction.h>
|
||||
#include <attributes/racename.h>
|
||||
#include <spells/buildingcurse.h>
|
||||
#include <spells/regioncurse.h>
|
||||
#include <spells/unitcurse.h>
|
||||
|
||||
/* kernel includes */
|
||||
#include "kernel/ally.h"
|
||||
#include "kernel/attrib.h"
|
||||
#include "kernel/building.h"
|
||||
#include "kernel/calendar.h"
|
||||
#include "kernel/config.h"
|
||||
#include "kernel/curse.h"
|
||||
#include "kernel/equipment.h"
|
||||
#include "kernel/event.h"
|
||||
#include "kernel/faction.h"
|
||||
#include "kernel/item.h"
|
||||
#include "kernel/messages.h"
|
||||
#include "kernel/order.h"
|
||||
#include "kernel/plane.h"
|
||||
#include "kernel/pool.h"
|
||||
#include "kernel/race.h"
|
||||
#include "kernel/region.h"
|
||||
#include "kernel/resources.h"
|
||||
#include "kernel/ship.h"
|
||||
#include "kernel/terrain.h"
|
||||
#include "kernel/terrainid.h"
|
||||
#include "kernel/unit.h"
|
||||
|
||||
/* util includes */
|
||||
#include <util/base36.h>
|
||||
#include <util/goodies.h>
|
||||
#include <util/language.h>
|
||||
#include <util/lists.h>
|
||||
#include <util/log.h>
|
||||
#include "util/param.h"
|
||||
#include <util/parser.h>
|
||||
#include <util/rng.h>
|
||||
|
||||
/* libs includes */
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
#define RECRUIT_MERGE 1
|
||||
static int rules_recruit = -1;
|
||||
|
||||
typedef struct recruit_request {
|
||||
struct recruit_request *next;
|
||||
struct unit *unit;
|
||||
struct order *ord;
|
||||
int qty;
|
||||
} recruit_request;
|
||||
|
||||
typedef struct recruitment {
|
||||
struct recruitment *next;
|
||||
faction *f;
|
||||
recruit_request *requests;
|
||||
int total, assigned;
|
||||
} recruitment;
|
||||
|
||||
static void recruit_init(void)
|
||||
{
|
||||
if (rules_recruit < 0) {
|
||||
rules_recruit = 0;
|
||||
if (config_get_int("recruit.allow_merge", 1)) {
|
||||
rules_recruit |= RECRUIT_MERGE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void free_requests(recruit_request *requests) {
|
||||
while (requests) {
|
||||
recruit_request *req = requests->next;
|
||||
free(requests);
|
||||
requests = req;
|
||||
}
|
||||
}
|
||||
|
||||
void free_recruitments(recruitment * recruits)
|
||||
{
|
||||
while (recruits) {
|
||||
recruitment *rec = recruits;
|
||||
recruits = rec->next;
|
||||
free_requests(rec->requests);
|
||||
free(rec);
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a list of recruitment structs, one for each faction. Adds every quantifyable production
|
||||
* to the faction's struct and to total.
|
||||
*/
|
||||
static recruitment *select_recruitment(recruit_request ** rop,
|
||||
int(*quantify) (const struct race *, int), int *total)
|
||||
{
|
||||
recruitment *recruits = NULL;
|
||||
|
||||
while (*rop) {
|
||||
recruitment *rec = recruits;
|
||||
recruit_request *ro = *rop;
|
||||
unit *u = ro->unit;
|
||||
const race *rc = u_race(u);
|
||||
int qty = quantify(rc, ro->qty);
|
||||
|
||||
if (qty < 0) {
|
||||
rop = &ro->next; /* skip this one */
|
||||
}
|
||||
else {
|
||||
*rop = ro->next; /* remove this one */
|
||||
while (rec && rec->f != u->faction)
|
||||
rec = rec->next;
|
||||
if (rec == NULL) {
|
||||
rec = (recruitment *)malloc(sizeof(recruitment));
|
||||
if (!rec) abort();
|
||||
rec->f = u->faction;
|
||||
rec->total = 0;
|
||||
rec->assigned = 0;
|
||||
rec->requests = NULL;
|
||||
rec->next = recruits;
|
||||
recruits = rec;
|
||||
}
|
||||
*total += qty;
|
||||
rec->total += qty;
|
||||
ro->next = rec->requests;
|
||||
rec->requests = ro;
|
||||
}
|
||||
}
|
||||
return recruits;
|
||||
}
|
||||
|
||||
void add_recruits(unit * u, int number, int wanted)
|
||||
{
|
||||
region *r = u->region;
|
||||
assert(number <= wanted);
|
||||
if (number > 0) {
|
||||
unit *unew;
|
||||
char equipment[64];
|
||||
int len;
|
||||
|
||||
if (u->number == 0) {
|
||||
set_number(u, number);
|
||||
u->hp = number * unit_max_hp(u);
|
||||
unew = u;
|
||||
}
|
||||
else {
|
||||
unew = create_unit(r, u->faction, number, u_race(u), 0, NULL, u);
|
||||
}
|
||||
|
||||
len = snprintf(equipment, sizeof(equipment), "new_%s", u_race(u)->_name);
|
||||
if (len > 0 && (size_t)len < sizeof(equipment)) {
|
||||
equip_unit(unew, equipment);
|
||||
}
|
||||
if (unew != u) {
|
||||
transfermen(unew, u, unew->number);
|
||||
remove_unit(&r->units, unew);
|
||||
}
|
||||
}
|
||||
if (number < wanted) {
|
||||
ADDMSG(&u->faction->msgs, msg_message("recruit",
|
||||
"unit region amount want", u, r, number, wanted));
|
||||
}
|
||||
}
|
||||
|
||||
static int any_recruiters(const struct race *rc, int qty)
|
||||
{
|
||||
return (int)(qty * 2 * rc->recruit_multi);
|
||||
}
|
||||
|
||||
static int do_recruiting(recruitment * recruits, int available)
|
||||
{
|
||||
recruitment *rec;
|
||||
int recruited = 0;
|
||||
|
||||
/* try to assign recruits to factions fairly */
|
||||
while (available > 0) {
|
||||
int n = 0;
|
||||
int rest, mintotal = INT_MAX;
|
||||
|
||||
/* find smallest production */
|
||||
for (rec = recruits; rec != NULL; rec = rec->next) {
|
||||
int want = rec->total - rec->assigned;
|
||||
if (want > 0) {
|
||||
if (mintotal > want)
|
||||
mintotal = want;
|
||||
++n;
|
||||
}
|
||||
}
|
||||
if (n == 0)
|
||||
break;
|
||||
if (mintotal * n > available) {
|
||||
mintotal = available / n;
|
||||
}
|
||||
rest = available - mintotal * n;
|
||||
|
||||
/* assign size of smallest production for everyone if possible; in the end roll dice to assign
|
||||
* small rest */
|
||||
for (rec = recruits; rec != NULL; rec = rec->next) {
|
||||
int want = rec->total - rec->assigned;
|
||||
|
||||
if (want > 0) {
|
||||
int get = mintotal;
|
||||
if (want > mintotal && rest < n && (rng_int() % n) < rest) {
|
||||
--rest;
|
||||
++get;
|
||||
}
|
||||
assert(get <= want);
|
||||
available -= get;
|
||||
rec->assigned += get;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* do actual recruiting */
|
||||
for (rec = recruits; rec != NULL; rec = rec->next) {
|
||||
recruit_request *req;
|
||||
int get = rec->assigned;
|
||||
|
||||
for (req = rec->requests; req; req = req->next) {
|
||||
unit *u = req->unit;
|
||||
const race *rc = u_race(u); /* race is set in recruit() */
|
||||
int number;
|
||||
double multi = 2.0 * rc->recruit_multi;
|
||||
|
||||
number = (int)(get / multi);
|
||||
if (number > req->qty) number = req->qty;
|
||||
if (rc->recruitcost) {
|
||||
int afford = get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT,
|
||||
number * rc->recruitcost) / rc->recruitcost;
|
||||
if (number > afford) number = afford;
|
||||
}
|
||||
if (u->number + number > UNIT_MAXSIZE) {
|
||||
ADDMSG(&u->faction->msgs, msg_feedback(u, req->ord, "error_unit_size",
|
||||
"maxsize", UNIT_MAXSIZE));
|
||||
number = UNIT_MAXSIZE - u->number;
|
||||
assert(number >= 0);
|
||||
}
|
||||
if (rc->recruitcost) {
|
||||
use_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT,
|
||||
rc->recruitcost * number);
|
||||
}
|
||||
if (u->number == 0 && fval(u, UFL_DEAD)) {
|
||||
/* unit is empty, dead, and cannot recruit */
|
||||
number = 0;
|
||||
}
|
||||
add_recruits(u, number, req->qty);
|
||||
if (number > 0) {
|
||||
int dec = (int)(number * multi);
|
||||
if ((rc->ec_flags & ECF_REC_ETHEREAL) == 0) {
|
||||
recruited += dec;
|
||||
}
|
||||
|
||||
get -= dec;
|
||||
}
|
||||
}
|
||||
}
|
||||
return recruited;
|
||||
}
|
||||
|
||||
/* Rekrutierung */
|
||||
static void expandrecruit(region * r, recruit_request * recruitorders)
|
||||
{
|
||||
recruitment *recruits;
|
||||
int orc_total = 0;
|
||||
|
||||
/* peasant limited: */
|
||||
recruits = select_recruitment(&recruitorders, any_recruiters, &orc_total);
|
||||
if (recruits) {
|
||||
int orc_recruited, orc_peasants = rpeasants(r) * 2;
|
||||
int orc_frac = orc_peasants / RECRUITFRACTION; /* anzahl orks. 2 ork = 1 bauer */
|
||||
if (orc_total < orc_frac)
|
||||
orc_frac = orc_total;
|
||||
orc_recruited = do_recruiting(recruits, orc_frac);
|
||||
assert(orc_recruited <= orc_frac);
|
||||
rsetpeasants(r, (orc_peasants - orc_recruited) / 2);
|
||||
free_recruitments(recruits);
|
||||
}
|
||||
|
||||
/* no limit: */
|
||||
recruits = select_recruitment(&recruitorders, any_recruiters, &orc_total);
|
||||
if (recruits) {
|
||||
int recruited, peasants = rpeasants(r) * 2;
|
||||
recruited = do_recruiting(recruits, INT_MAX);
|
||||
if (recruited > 0) {
|
||||
rsetpeasants(r, (peasants - recruited) / 2);
|
||||
}
|
||||
free_recruitments(recruits);
|
||||
}
|
||||
|
||||
assert(recruitorders == NULL);
|
||||
}
|
||||
|
||||
static int recruit_cost(const faction * f, const race * rc)
|
||||
{
|
||||
if (is_monsters(f) || valid_race(f, rc)) {
|
||||
return rc->recruitcost;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
message *can_recruit(unit *u, const race *rc, order *ord, int now)
|
||||
{
|
||||
region *r = u->region;
|
||||
|
||||
/* this is a very special case because the recruiting unit may be empty
|
||||
* at this point and we have to look at the creating unit instead. This
|
||||
* is done in cansee, which is called indirectly by is_guarded(). */
|
||||
if (is_guarded(r, u)) {
|
||||
return msg_error(u, ord, 70);
|
||||
}
|
||||
|
||||
if (rc == get_race(RC_INSECT)) {
|
||||
gamedate date;
|
||||
get_gamedate(now, &date);
|
||||
if (date.season == SEASON_WINTER && r->terrain != newterrain(T_DESERT)) {
|
||||
bool usepotion = false;
|
||||
unit *u2;
|
||||
|
||||
for (u2 = r->units; u2; u2 = u2->next) {
|
||||
if (fval(u2, UFL_WARMTH)) {
|
||||
usepotion = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!usepotion) {
|
||||
return msg_error(u, ord, 98);
|
||||
}
|
||||
}
|
||||
/* in Gletschern, Eisbergen gar nicht rekrutieren */
|
||||
if (r_insectstalled(r)) {
|
||||
return msg_error(u, ord, 97);
|
||||
}
|
||||
}
|
||||
if (is_cursed(r->attribs, &ct_riotzone)) {
|
||||
/* Die Region befindet sich in Aufruhr */
|
||||
return msg_error(u, ord, 237);
|
||||
}
|
||||
|
||||
if (rc && !playerrace(rc)) {
|
||||
return msg_error(u, ord, 139);
|
||||
}
|
||||
|
||||
if (fval(u, UFL_HERO)) {
|
||||
return msg_feedback(u, ord, "error_herorecruit", "");
|
||||
}
|
||||
if (has_skill(u, SK_MAGIC)) {
|
||||
/* error158;de;{unit} in {region}: '{command}' - Magier arbeiten
|
||||
* grundsaetzlich nur alleine! */
|
||||
return msg_error(u, ord, 158);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void recruit_cmd(unit * u, struct order *ord, recruit_request ** recruitorders)
|
||||
{
|
||||
region *r = u->region;
|
||||
recruit_request *o;
|
||||
int recruitcost = -1;
|
||||
const faction *f = u->faction;
|
||||
const struct race *rc = u_race(u);
|
||||
int n;
|
||||
message *msg;
|
||||
|
||||
init_order_depr(ord);
|
||||
n = getint();
|
||||
if (n <= 0) {
|
||||
syntax_error(u, ord);
|
||||
return;
|
||||
}
|
||||
|
||||
if (u->number == 0) {
|
||||
char token[128];
|
||||
const char *str;
|
||||
|
||||
str = gettoken(token, sizeof(token));
|
||||
if (str && str[0]) {
|
||||
/* Monsters can RECRUIT 15 DRACOID
|
||||
* also: secondary race */
|
||||
rc = findrace(str, f->locale);
|
||||
if (rc != NULL) {
|
||||
recruitcost = recruit_cost(f, rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (recruitcost < 0) {
|
||||
rc = u_race(u);
|
||||
recruitcost = recruit_cost(f, rc);
|
||||
if (recruitcost < 0) {
|
||||
recruitcost = INT_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
if (recruitcost > 0) {
|
||||
int pool;
|
||||
plane *pl = getplane(r);
|
||||
|
||||
if (pl && (pl->flags & PFL_NORECRUITS)) {
|
||||
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_pflnorecruit", ""));
|
||||
return;
|
||||
}
|
||||
|
||||
pool = get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT, recruitcost * n);
|
||||
if (pool < recruitcost) {
|
||||
cmistake(u, ord, 142, MSG_EVENT);
|
||||
return;
|
||||
}
|
||||
pool /= recruitcost;
|
||||
if (n > pool) n = pool;
|
||||
}
|
||||
|
||||
if (!n) {
|
||||
cmistake(u, ord, 142, MSG_EVENT);
|
||||
return;
|
||||
}
|
||||
if (has_skill(u, SK_ALCHEMY)) {
|
||||
if (faction_count_skill(u->faction, SK_ALCHEMY) + n > faction_skill_limit(u->faction, SK_ALCHEMY)) {
|
||||
cmistake(u, ord, 156, MSG_EVENT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
assert(rc);
|
||||
msg = can_recruit(u, rc, ord, turn);
|
||||
if (msg) {
|
||||
add_message(&u->faction->msgs, msg);
|
||||
msg_release(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
u_setrace(u, rc);
|
||||
u->wants = n;
|
||||
o = (recruit_request *)calloc(1, sizeof(recruit_request));
|
||||
if (!o) abort();
|
||||
o->qty = n;
|
||||
o->unit = u;
|
||||
o->ord = ord;
|
||||
addlist(recruitorders, o);
|
||||
}
|
||||
|
||||
void recruit(region * r)
|
||||
{
|
||||
unit *u;
|
||||
recruit_request *recruitorders = NULL;
|
||||
|
||||
if (rules_recruit < 0)
|
||||
recruit_init();
|
||||
for (u = r->units; u; u = u->next) {
|
||||
order *ord;
|
||||
|
||||
if ((rules_recruit & RECRUIT_MERGE) || u->number == 0) {
|
||||
for (ord = u->orders; ord; ord = ord->next) {
|
||||
if (getkeyword(ord) == K_RECRUIT) {
|
||||
recruit_cmd(u, ord, &recruitorders);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (recruitorders) {
|
||||
expandrecruit(r, recruitorders);
|
||||
}
|
||||
remove_empty_units_in_region(r);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue