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