diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..16ec084e0 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,16 @@ +# Geplante Aenderungen in zukuenftigen Eressea-Versionen + +Als Anhaltspunkt fuer die Empfaenger der woechentlichen Testauswertungnen +will ich versuchen, die Liste meiner Aenderungen seit dem letzten Release +zu dokumentieren. + +## Version 3.12.0 + +- [other] optimierte Berechnung der Sichtbarkeit von Leuchttuermen +- [bug] Einheitenlimit bei GIB PERSON beachten +- [bug] Einheitenlimit bei ALLIANCE JOIN beachten +- [rule] Einheiten- und Personenzahl im Report beinhaltet *alle* Einheiten der Partei. +- [other] Statistik fuer Spielleiter zeigt das Parteisilber nicht mehr an. +- [other] Berechnung der Message-Ids optimiert. + + diff --git a/conf/calendar.json b/conf/calendar.json new file mode 100644 index 000000000..43b01351a --- /dev/null +++ b/conf/calendar.json @@ -0,0 +1,22 @@ +{ + "calendar" : { + "months" : [ + { "storm" : 60, "season" : 2 }, + + { "storm" : 10, "season" : 3 }, + { "storm" : 60, "season" : 3 }, + + { "storm" : 10, "season" : 0 }, + { "storm" : 60, "season" : 0 }, + { "storm" : 80, "season" : 0 }, + + { "storm" : 50, "season" : 1 }, + { "storm" : 30, "season" : 1 }, + + { "storm" : 60, "season" : 2 } + ], + "weeks" : [ + "firstweek", "secondweek", "thirdweek" + ] + } +} diff --git a/conf/e2/config.json b/conf/e2/config.json index f9f9b2df0..fb231e2eb 100644 --- a/conf/e2/config.json +++ b/conf/e2/config.json @@ -1,9 +1,10 @@ { - "include": [ - "keywords.json", - "prefixes.json", + "include": [ + "keywords.json", + "calendar.json", + "prefixes.json", "e2/terrains.json" - ], + ], "disabled": [ "jsreport" ], @@ -21,13 +22,12 @@ "GiveRestriction": 3, "hunger.long": true, "init_spells": 0, - "world.era": 2, - "seed.population.min": 8, - "seed.population.max": 8, + "game.era": 2, + "game.start": 184, "rules.reserve.twophase": true, "rules.give.max_men": -1, "rules.check_overload": false, - "rules.limit.faction": 2500, + "rules.limit.faction": 3000, "rules.maxskills.magic": 5, "rules.guard.base_stop_prob": 0.30, "rules.guard.skill_stop_prob": 0.05, diff --git a/conf/e2/rules.xml b/conf/e2/rules.xml index 8d0b01b27..7bde2205f 100644 --- a/conf/e2/rules.xml +++ b/conf/e2/rules.xml @@ -15,8 +15,6 @@ - - diff --git a/conf/e3/config.json b/conf/e3/config.json index 1cecc89d2..bfd1bd0fc 100644 --- a/conf/e3/config.json +++ b/conf/e3/config.json @@ -1,6 +1,7 @@ { "include": [ "keywords.json", + "calendar.json", "prefixes.json", "e3/terrains.json" ], @@ -51,11 +52,11 @@ "recruit.allow_merge": true, "study.expensivemigrants": true, "study.speedup": 2, - "world.era": 3, - "seed.population.min": 8, - "seed.population.max": 8, + "game.era": 3, + "game.start": 1, "rules.reserve.twophase": true, "rules.owners.force_leave": false, + "rules.wage.function": 2, "rules.monsters.attack_chance": 0.1, "rules.transfermen": false, "stealth.faction.other": false, @@ -87,7 +88,7 @@ "rules.economy.herbrot": 0, "rules.region_owner_pay_building": "market harbour lighthouse", "rules.dwarf_castles": true, - "rules.limit.faction": 250, + "rules.limit.faction": 500, "rules.grow.formula": 1, "rules.tactics.formula": 1, "rules.help.mask": "fight guard money give", diff --git a/conf/e3/rules.xml b/conf/e3/rules.xml index 421b54d7a..7fbf7dc6e 100644 --- a/conf/e3/rules.xml +++ b/conf/e3/rules.xml @@ -10,7 +10,6 @@ - @@ -21,14 +20,9 @@ - - - - - diff --git a/conf/eressea.ini b/conf/eressea.ini index b6db180ca..c745631ad 100644 --- a/conf/eressea.ini +++ b/conf/eressea.ini @@ -2,11 +2,9 @@ email = eressea-server@kn-bremen.de sender = Eressea Server name = Eressea -base = . report = reports verbose = 0 lomem = 0 -debug = 0 memcheck = 0 locales = de,en diff --git a/doc/gprof-v3.10.0-163-gdfab45d.txt b/doc/gprof-v3.10.0-163-gdfab45d.profile similarity index 100% rename from doc/gprof-v3.10.0-163-gdfab45d.txt rename to doc/gprof-v3.10.0-163-gdfab45d.profile diff --git a/etc/report-mail.de.txt b/etc/report-mail.de.txt new file mode 100644 index 000000000..e40d86b53 --- /dev/null +++ b/etc/report-mail.de.txt @@ -0,0 +1,10 @@ +Freust Du Dich, dass es diese Woche wieder einen Eressea-Report gibt? +Möchtest Du dazu beitragen, dass das auch nächste Woche wieder passiert? +Eressea ist ein freiwilliges gratis-Angebot, und die Spielleitung bezahlt +seit Jahren die Entwicklung und das Hosting aus eigener Tasche. Wenn Dir +das etwas wert ist, kannst Du das auf +https://flattr.com/thing/681354/Eressea zum Ausdruck bringen. + +Diese Mail enthält ein Attachment mit Deinem Eressea-Report in +komprimierter Form. Um ihn zu entpacken benötigst Du ein Programm, +das ZIP-Archive öffnen kann, wie z.B. 7-Zip (http://www.7-zip.org/) diff --git a/etc/report-mail.en.txt b/etc/report-mail.en.txt new file mode 100644 index 000000000..58a8e0c19 --- /dev/null +++ b/etc/report-mail.en.txt @@ -0,0 +1,6 @@ +If you feel that getting a report every weekend is worth something to you, +why not flattr Eressea at https://flattr.com/thing/681354/Eressea ? + +This email contains an Attachment with your Eressea report in compressed +form. To open the attachment, you will need a program that can extract +ZIP files, e.g. 7-Zip (http://www.7-zip.org/). diff --git a/etc/report-mail.txt b/etc/report-mail.txt new file mode 100644 index 000000000..4101243e7 --- /dev/null +++ b/etc/report-mail.txt @@ -0,0 +1,14 @@ +Freust Du Dich, dass es diese Woche wieder einen Eressea-Report gibt? +Möchtest Du dazu beitragen, dass das auch nächste Woche wieder passiert? +Eressea ist ein freiwilliges gratis-Angebot, und die Spielleitung bezahlt +seit Jahren die Entwicklung und das Hosting aus eigener Tasche. Wenn Dir +das etwas wert ist, kannst Du das auf +https://flattr.com/thing/681354/Eressea zum Ausdruck bringen. + +Diese Mail enthält ein Attachment mit Deinem Eressea-Report in +komprimierter Form. Um ihn zu entpacken benötigst Du ein Programm, +das ZIP-Archive öffnen kann, wie z.B. 7-Zip (http://www.7-zip.org/) + +This email contains an attachment with your Eressea report in compressed +form. To open the attachment, you will need a program that can extract +ZIP files, e.g. 7-Zip (http://www.7-zip.org/). diff --git a/process/cron/crontab b/process/cron/crontab deleted file mode 100644 index 9d7e155cb..000000000 --- a/process/cron/crontab +++ /dev/null @@ -1,17 +0,0 @@ -# Crontab for Eressea on gruenbaer.kn-bremen.de - -PATH=/home/eressea/bin:/opt/bin:/usr/local/bin:/usr/bin:/bin -ERESSEA=/home/eressea/eressea -ENABLED=yes -PREVIEW=yes -CONFIRM=yes - -# m h dom mon dow command -00 00 * * * $HOME/bin/fetchmail.cron -00 22 * * * $HOME/bin/backup-db.sh - -15 21 * * Sat [ "$ENABLED" = "yes" ] && $ERESSEA/server/bin/run-eressea.cron 3 -25 21 * * Sat [ "$ENABLED" = "yes" ] && $ERESSEA/server/bin/run-eressea.cron 4 -35 21 * * Sat [ "$ENABLED" = "yes" ] && $ERESSEA/server/bin/run-eressea.cron 2 -30 07 * * Sun [ "$PREVIEW" = "yes" ] && $ERESSEA/server/bin/preview.cron -*/5 * * * * [ "$CONFIRM" = "yes" ] && $ERESSEA/server/bin/orders.cron 2 3 4 diff --git a/process/cron/orders.cron b/process/cron/orders.cron index be573fe86..114a870a5 100755 --- a/process/cron/orders.cron +++ b/process/cron/orders.cron @@ -5,6 +5,10 @@ # this here script to make a non-blocking syntax check and reject or # accept the order file. +if [ "yes" != "$CONFIRM" ] ; then + exit +fi + for GAME in $* do if [ "$GAME" == "eressea" ]; then GAME=2 ; fi diff --git a/process/cron/preview.cron b/process/cron/preview.cron index 985ddf40b..71ebd0571 100755 --- a/process/cron/preview.cron +++ b/process/cron/preview.cron @@ -1,6 +1,8 @@ #!/bin/bash -[ -z ${ERESSEA} ] && ERESSEA=~/eressea +[ "$PREVIEW" != "yes" ] && exit +[ -z ${ERESSEA} ] && ERESSEA=$HOME/eressea + branch="develop" if [ -e ${ERESSEA}/build/.preview ]; then branch=`cat ${ERESSEA}/build/.preview` diff --git a/process/cron/run-eressea.cron b/process/cron/run-eressea.cron index 8b506a633..4ea38184c 100755 --- a/process/cron/run-eressea.cron +++ b/process/cron/run-eressea.cron @@ -1,7 +1,11 @@ #!/bin/bash GAME=$1 -BIN=$HOME/bin -export ERESSEA=$HOME/eressea + +[ "$ENABLED" != "yes" ] && exit +[ -z ${ERESSEA} ] && ERESSEA=$HOME/eressea + +export ERESSEA +BIN=$ERESSEA/server/bin TURN=$(cat $ERESSEA/game-$GAME/turn) if [ ! -e $ERESSEA/game-$GAME/data/$TURN.dat ]; then echo "data file $TURN is missing, cannot run turn for game $GAME" @@ -14,6 +18,18 @@ fi mkdir $REPORTS cd $ERESSEA/game-$GAME + +# wait for the queue lock to go away +maxt=5 +to=5 +while [ -e orders.queue.lock ] ; do + echo "waiting for orders to finish processing." + sleep $to + let to=$to+$to + let mast=$maxt-1 + [ $maxt -lt 0 ] && break +done + if [ -d test ]; then touch test/execute.lock fi diff --git a/process/orders.cron b/process/orders.cron deleted file mode 100755 index be573fe86..000000000 --- a/process/orders.cron +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -## this script processes incoming order files. -# files are delivered into an incoming queue by procmail, then cron runs -# this here script to make a non-blocking syntax check and reject or -# accept the order file. - -for GAME in $* -do - if [ "$GAME" == "eressea" ]; then GAME=2 ; fi - if [ "$GAME" == "e3a" ]; then GAME=3 ; fi - if [ -e $HOME/eressea/game-$GAME/orders.queue ] - then - $HOME/bin/orders-process $GAME - fi -done diff --git a/process/run-turn b/process/run-turn index 0c5ee8fd2..c0d0d6a40 100755 --- a/process/run-turn +++ b/process/run-turn @@ -1,3 +1,5 @@ +#!/bin/sh + GAME=$1 TURN=$2 @@ -12,3 +14,4 @@ echo "running turn $TURN, game $GAME" $ERESSEA/server/bin/eressea -v3 -t $TURN run-turn.lua mkdir -p log ln -f eressea.log log/eressea.log.$TURN + diff --git a/process/run-turn.sh b/process/run-turn.sh deleted file mode 100755 index 9cc4e4ffd..000000000 --- a/process/run-turn.sh +++ /dev/null @@ -1,22 +0,0 @@ -GAME=$1 -TURN=$2 - -if [ ! -d $ERESSEA/game-$GAME ] ; then - echo "No such game: $GAME" - exit 1 -fi - -cd $ERESSEA/game-$GAME -if [ -z $TURN ]; then - TURN=$(cat turn) -fi - -echo "running turn $TURN, game $GAME" -if [ -d orders.dir.$TURN ]; then - echo "orders.dir.$TURN already exists" -else - mv orders.dir orders.dir.$TURN - mkdir -p orders.dir -fi -ls -1rt orders.dir.$TURN/turn-* | xargs cat > orders.$TURN -$ERESSEA/bin/eressea -t $TURN run-turn.lua diff --git a/res/adamantium.xml b/res/adamantium.xml index 13d08ca99..e1f0643e2 100644 --- a/res/adamantium.xml +++ b/res/adamantium.xml @@ -5,10 +5,8 @@ - - diff --git a/res/buildings/castle-2.xml b/res/buildings/castle-2.xml index 7b015d199..7e64f88af 100644 --- a/res/buildings/castle-2.xml +++ b/res/buildings/castle-2.xml @@ -1,23 +1,21 @@ - - - - + + - + - + - + - + - + diff --git a/res/buildings/castle.xml b/res/buildings/castle.xml index 6e9140222..78ce7066c 100644 --- a/res/buildings/castle.xml +++ b/res/buildings/castle.xml @@ -1,25 +1,24 @@ - - + - + - + - + - + - + - + diff --git a/res/core/armor/plate.xml b/res/core/armor/plate.xml index a22064e90..97d855a73 100644 --- a/res/core/armor/plate.xml +++ b/res/core/armor/plate.xml @@ -1,7 +1,6 @@ - diff --git a/res/core/calendar.xml b/res/core/calendar.xml deleted file mode 100644 index d96037698..000000000 --- a/res/core/calendar.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/res/core/common/buildings.xml b/res/core/common/buildings.xml index 540e9f085..a799ce309 100644 --- a/res/core/common/buildings.xml +++ b/res/core/common/buildings.xml @@ -93,7 +93,6 @@ - @@ -102,6 +101,8 @@ + + diff --git a/res/core/common/items.xml b/res/core/common/items.xml index c903a6b78..35f9af2c5 100644 --- a/res/core/common/items.xml +++ b/res/core/common/items.xml @@ -61,16 +61,8 @@ - - - - - - - - - + @@ -78,9 +70,7 @@ - - - + @@ -100,39 +90,34 @@ + + + + + - - - + - - - + - - - + - - - + - - - + diff --git a/res/core/common/potions.xml b/res/core/common/potions.xml index 84b1e5f52..4812727dd 100644 --- a/res/core/common/potions.xml +++ b/res/core/common/potions.xml @@ -4,8 +4,7 @@ - - + @@ -15,8 +14,7 @@ - - + @@ -26,8 +24,7 @@ - - + @@ -37,8 +34,7 @@ - - + @@ -48,8 +44,7 @@ - - + @@ -61,8 +56,7 @@ - - + @@ -74,8 +68,7 @@ - - + @@ -87,8 +80,7 @@ - - + @@ -100,8 +92,7 @@ - - + @@ -113,8 +104,7 @@ - - + @@ -126,8 +116,7 @@ - - + @@ -139,8 +128,7 @@ - - + @@ -152,8 +140,7 @@ - - + @@ -166,8 +153,7 @@ - - + @@ -181,8 +167,7 @@ - - + diff --git a/res/core/de/strings.xml b/res/core/de/strings.xml index a39657580..c26bfe03b 100644 --- a/res/core/de/strings.xml +++ b/res/core/de/strings.xml @@ -1458,12 +1458,6 @@ Amulette des wahren Sehens - - Katzenamulett - - - Katzenamulette - Ring der Unsichtbarkeit @@ -6529,13 +6523,17 @@ Sonnenfeuer sunfire - + + neuer Zeitrechnung + of the new age + + des zweiten Zeitalters the second age - - neuer Zeitrechnung - of the new age + + des dritten Zeitalters + the third age diff --git a/res/core/en/strings.xml b/res/core/en/strings.xml index d1f744df1..dbc4dc611 100644 --- a/res/core/en/strings.xml +++ b/res/core/en/strings.xml @@ -444,12 +444,6 @@ amulets of chastity - - amulet of the kitten - - - amulets of the kitten - amulet of darkness diff --git a/res/core/fr/strings.xml b/res/core/fr/strings.xml index c69bb5b75..13e6beb25 100644 --- a/res/core/fr/strings.xml +++ b/res/core/fr/strings.xml @@ -458,12 +458,6 @@ amulettes de chasteté - - amulette du chaton - - - amulettes du chaton - amulette de ténčbres diff --git a/res/core/messages.xml b/res/core/messages.xml index 32d8e35a6..5d8a60ef0 100644 --- a/res/core/messages.xml +++ b/res/core/messages.xml @@ -5536,8 +5536,8 @@ - "$unit($unit) in $region($region): '$order($command)' - Nur Elfen können diese Bögen herstellen." - "$unit($unit) in $region($region): '$order($command)' - Only elves can make these bows." + "$unit($unit) in $region($region): '$order($command)' - Diesen Gegenstand kann die Einheit nicht herstellen." + "$unit($unit) in $region($region): '$order($command)' - This unit cannot produce that." @@ -5545,8 +5545,8 @@ - "$unit($unit) in $region($region): '$order($command)' - Nur die EMail-Adresse angeben!" - "$unit($unit) in $region($region): '$order($command)' - Please provide only an email address!" + "$unit($unit) in $region($region): '$order($command)' - Diese Rasse kann das nicht herstellen." + "$unit($unit) in $region($region): '$order($command)' - This race cannot produce that." @@ -7678,24 +7678,6 @@ "$unit($unit) plays the bagpipe. Stricken with fear the peasants give $int($money) silver." - - - - - - "$unit($unit) in $region($region) erschafft eine Akademie der Künste." - "$unit($unit) in $region($region) creates an academy of arts." - - - - - - - - "$unit($unit) in $region($region) erschafft eine Skulptur." - "$unit($unit) in $region($region) creates a sculpture." - - diff --git a/res/core/resources/horse.xml b/res/core/resources/horse.xml index d7f794491..608fca20a 100644 --- a/res/core/resources/horse.xml +++ b/res/core/resources/horse.xml @@ -1,11 +1,6 @@ - - + - - - - diff --git a/res/core/resources/hp.xml b/res/core/resources/hp.xml index fb1b6e32f..aa0ad4d29 100644 --- a/res/core/resources/hp.xml +++ b/res/core/resources/hp.xml @@ -1,5 +1,4 @@ - diff --git a/res/core/resources/iron.xml b/res/core/resources/iron.xml index 469840669..1aff826fc 100644 --- a/res/core/resources/iron.xml +++ b/res/core/resources/iron.xml @@ -1,11 +1,10 @@ - - - - + + + + - - + diff --git a/res/core/resources/laen.xml b/res/core/resources/laen.xml index 5fbe86549..e584fe485 100644 --- a/res/core/resources/laen.xml +++ b/res/core/resources/laen.xml @@ -1,10 +1,8 @@ - - - - - - - + + + + + diff --git a/res/core/resources/log.xml b/res/core/resources/log.xml index 48662878f..e3710d496 100644 --- a/res/core/resources/log.xml +++ b/res/core/resources/log.xml @@ -1,12 +1,8 @@ - - - - - + + + + - - - diff --git a/res/core/resources/mallorn.xml b/res/core/resources/mallorn.xml index 96892b802..3dff091c6 100644 --- a/res/core/resources/mallorn.xml +++ b/res/core/resources/mallorn.xml @@ -1,12 +1,8 @@ - - - - - + + + + - - - diff --git a/res/core/resources/mallornseed.xml b/res/core/resources/mallornseed.xml index 01b8a3416..7a5a0310f 100644 --- a/res/core/resources/mallornseed.xml +++ b/res/core/resources/mallornseed.xml @@ -3,8 +3,4 @@ - - - - diff --git a/res/core/resources/peasant.xml b/res/core/resources/peasant.xml index 32e1e1ca1..bce23430c 100644 --- a/res/core/resources/peasant.xml +++ b/res/core/resources/peasant.xml @@ -1,5 +1,4 @@ - diff --git a/res/core/resources/seed.xml b/res/core/resources/seed.xml index 2bda26eeb..99f5f5804 100644 --- a/res/core/resources/seed.xml +++ b/res/core/resources/seed.xml @@ -3,8 +3,4 @@ - - - - diff --git a/res/core/resources/stone.xml b/res/core/resources/stone.xml index 91d3aac5d..e1c5651ed 100644 --- a/res/core/resources/stone.xml +++ b/res/core/resources/stone.xml @@ -1,11 +1,9 @@ - - - - + + + - diff --git a/res/core/spells.xml b/res/core/spells.xml index cb7fcf911..59d06a522 100644 --- a/res/core/spells.xml +++ b/res/core/spells.xml @@ -10,14 +10,12 @@ - - @@ -25,12 +23,10 @@ - - @@ -38,19 +34,16 @@ - - - @@ -58,18 +51,15 @@ - - - @@ -77,12 +67,10 @@ - - @@ -90,25 +78,21 @@ - - - - @@ -116,7 +100,6 @@ - @@ -124,20 +107,17 @@ - - - diff --git a/res/core/weapons/axe.xml b/res/core/weapons/axe.xml index 80a872ad7..3f97a5b8a 100644 --- a/res/core/weapons/axe.xml +++ b/res/core/weapons/axe.xml @@ -1,7 +1,6 @@ - diff --git a/res/core/weapons/greatbow.xml b/res/core/weapons/greatbow.xml index 10b270e55..452ac0250 100644 --- a/res/core/weapons/greatbow.xml +++ b/res/core/weapons/greatbow.xml @@ -1,9 +1,8 @@ + - - diff --git a/res/core/weapons/greatsword.xml b/res/core/weapons/greatsword.xml index cd48b8296..56285f6af 100644 --- a/res/core/weapons/greatsword.xml +++ b/res/core/weapons/greatsword.xml @@ -1,7 +1,6 @@ - diff --git a/res/core/weapons/halberd.xml b/res/core/weapons/halberd.xml index 5025f4793..7abc86902 100644 --- a/res/core/weapons/halberd.xml +++ b/res/core/weapons/halberd.xml @@ -1,7 +1,6 @@ - diff --git a/res/core/weapons/lance.xml b/res/core/weapons/lance.xml index abbb7f31f..4a02bc06f 100644 --- a/res/core/weapons/lance.xml +++ b/res/core/weapons/lance.xml @@ -1,7 +1,6 @@ - diff --git a/res/core/weapons/mallornlance.xml b/res/core/weapons/mallornlance.xml index 0186143e4..c67390d52 100644 --- a/res/core/weapons/mallornlance.xml +++ b/res/core/weapons/mallornlance.xml @@ -1,7 +1,6 @@ - diff --git a/res/core/weapons/rustyaxe.xml b/res/core/weapons/rustyaxe.xml index 2ff19570c..fea6b8642 100644 --- a/res/core/weapons/rustyaxe.xml +++ b/res/core/weapons/rustyaxe.xml @@ -1,7 +1,6 @@ - diff --git a/res/core/weapons/rustyhalberd.xml b/res/core/weapons/rustyhalberd.xml index c1b1f69a9..3f0bd93b7 100644 --- a/res/core/weapons/rustyhalberd.xml +++ b/res/core/weapons/rustyhalberd.xml @@ -1,7 +1,6 @@ - diff --git a/res/e3a/armor/plate.xml b/res/e3a/armor/plate.xml index 29cabbf1a..79391fbcb 100644 --- a/res/e3a/armor/plate.xml +++ b/res/e3a/armor/plate.xml @@ -1,7 +1,6 @@ - - + diff --git a/res/e3a/armor/scale.xml b/res/e3a/armor/scale.xml index 7039b4008..4d3e38e8a 100644 --- a/res/e3a/armor/scale.xml +++ b/res/e3a/armor/scale.xml @@ -1,9 +1,9 @@ - - + + + - diff --git a/res/e3a/armor/towershield.xml b/res/e3a/armor/towershield.xml index e80c03523..aebbfc682 100644 --- a/res/e3a/armor/towershield.xml +++ b/res/e3a/armor/towershield.xml @@ -1,9 +1,9 @@ - - + + + - diff --git a/res/e3a/buildings.xml b/res/e3a/buildings.xml index 9eeca952f..8641fd89e 100644 --- a/res/e3a/buildings.xml +++ b/res/e3a/buildings.xml @@ -3,16 +3,14 @@ - - - - + + - + - + diff --git a/res/e3a/items.xml b/res/e3a/items.xml index 7d7e6b286..0399043e5 100644 --- a/res/e3a/items.xml +++ b/res/e3a/items.xml @@ -7,83 +7,72 @@ - - + - - + - - + - - + - - + - - + - - + - - + - + - - - - + diff --git a/res/e3a/races.xml b/res/e3a/races.xml index 2e5a81aee..8b0cfae6d 100644 --- a/res/e3a/races.xml +++ b/res/e3a/races.xml @@ -71,7 +71,11 @@ - + @@ -116,7 +120,11 @@ - + @@ -156,7 +164,11 @@ - + diff --git a/res/e3a/resources/iron.xml b/res/e3a/resources/iron.xml index 7ffe36f1e..55f63ca67 100644 --- a/res/e3a/resources/iron.xml +++ b/res/e3a/resources/iron.xml @@ -1,10 +1,9 @@ - - - - + + + + - diff --git a/res/e3a/resources/stone.xml b/res/e3a/resources/stone.xml index dfb039093..d8fd08573 100644 --- a/res/e3a/resources/stone.xml +++ b/res/e3a/resources/stone.xml @@ -1,10 +1,8 @@ - - - - + + + - diff --git a/res/e3a/spells.xml b/res/e3a/spells.xml index 74eb89146..80d9e6260 100644 --- a/res/e3a/spells.xml +++ b/res/e3a/spells.xml @@ -10,14 +10,12 @@ - - @@ -39,7 +37,6 @@ - @@ -106,7 +103,6 @@ - @@ -119,7 +115,6 @@ - @@ -189,7 +184,6 @@ - @@ -205,7 +199,6 @@ - @@ -269,7 +262,6 @@ - @@ -304,12 +296,11 @@ - + - @@ -394,21 +385,18 @@ - - - @@ -416,7 +404,6 @@ - @@ -426,7 +413,6 @@ - @@ -435,7 +421,6 @@ - @@ -444,7 +429,6 @@ - @@ -454,7 +438,6 @@ - @@ -472,39 +455,33 @@ - - - - - - @@ -518,7 +495,7 @@ - + diff --git a/res/e3a/strings.xml b/res/e3a/strings.xml index 054cabfaa..e3c5b10ae 100644 --- a/res/e3a/strings.xml +++ b/res/e3a/strings.xml @@ -38,12 +38,6 @@ adamantium plates - - - des dritten Zeitalters - the third age - - Packeis fast ice @@ -87,6 +81,7 @@ repeating crossbows + Gerüst scaffolding @@ -103,6 +98,7 @@ Wache watch + Marktplatz marketplace @@ -210,7 +206,7 @@ Dieser Zauber wird die gesamte Ausrüstung der Zieleinheit für einige Zeit vor den Blicken anderer verschleiern. Der Zauber schützt nicht vor Dieben und Spionen. This spell will hide the whole equipment of a target unit from the looks of others. It will not protect against thieves or spies. - + Durch dieses uralte Tanzritual ruft der Zauberkundige die Kräfte des Lebens und der Fruchtbarkeit an. Die darauf folgenden Regenfälle begünstigen das Wachstum und erhöhen die Ernteerträge einiger Bauern der Region bis der Regen wieder nachlässt. This ancient rite calls upon the forces of life and fertility. For the next few weeks, the peasants' harvest will be extraordinary good. diff --git a/res/e3a/weapons.xml b/res/e3a/weapons.xml index 052266a02..fdc8f1f3a 100644 --- a/res/e3a/weapons.xml +++ b/res/e3a/weapons.xml @@ -1,18 +1,18 @@ - - - + + + diff --git a/res/e3a/weapons/axe.xml b/res/e3a/weapons/axe.xml new file mode 100644 index 000000000..6066c4cfc --- /dev/null +++ b/res/e3a/weapons/axe.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/res/e3a/weapons/greatbow.xml b/res/e3a/weapons/greatbow.xml index 7f08427c8..358a55f7d 100644 --- a/res/e3a/weapons/greatbow.xml +++ b/res/e3a/weapons/greatbow.xml @@ -4,10 +4,9 @@ * has lower damage --> - - + + - diff --git a/res/e3a/weapons/halberd.xml b/res/e3a/weapons/halberd.xml index 2c11b62fc..c22e020b4 100644 --- a/res/e3a/weapons/halberd.xml +++ b/res/e3a/weapons/halberd.xml @@ -3,8 +3,7 @@ 1. you cannt use this with cavalry --> - - + diff --git a/res/e3a/weapons/lance.xml b/res/e3a/weapons/lance.xml new file mode 100644 index 000000000..285862484 --- /dev/null +++ b/res/e3a/weapons/lance.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/res/e3a/weapons/mallornlance.xml b/res/e3a/weapons/mallornlance.xml index 1fcad08c8..8eb25ceb4 100644 --- a/res/e3a/weapons/mallornlance.xml +++ b/res/e3a/weapons/mallornlance.xml @@ -1,7 +1,6 @@ - - + diff --git a/res/core/weapons/rep_crossbow.xml b/res/e3a/weapons/rep_crossbow.xml similarity index 80% rename from res/core/weapons/rep_crossbow.xml rename to res/e3a/weapons/rep_crossbow.xml index 66e3f9fc2..ffa1c0455 100644 --- a/res/core/weapons/rep_crossbow.xml +++ b/res/e3a/weapons/rep_crossbow.xml @@ -1,9 +1,8 @@ - - + + - diff --git a/res/e3a/weapons/rustyhalberd.xml b/res/e3a/weapons/rustyhalberd.xml index ed25d5db7..a53aec515 100644 --- a/res/e3a/weapons/rustyhalberd.xml +++ b/res/e3a/weapons/rustyhalberd.xml @@ -3,8 +3,7 @@ 1. you cannot use this with cavalry --> - - + diff --git a/res/eressea/artrewards.xml b/res/eressea/artrewards.xml index 2a3c09950..b18fd5b20 100644 --- a/res/eressea/artrewards.xml +++ b/res/eressea/artrewards.xml @@ -3,16 +3,12 @@ - - - + - - - + diff --git a/res/eressea/items.xml b/res/eressea/items.xml index 69f84c890..3f5cc3d17 100644 --- a/res/eressea/items.xml +++ b/res/eressea/items.xml @@ -17,21 +17,15 @@ - - - + - - - + - - - + @@ -44,52 +38,38 @@ - - - + - - - + - - - + - - - + - - - + - - - + - - - + @@ -98,27 +78,13 @@ - - - - - - - - - - - - - - + + - - - + diff --git a/res/eressea/races.xml b/res/eressea/races.xml index 6deefacc9..986e2b85f 100644 --- a/res/eressea/races.xml +++ b/res/eressea/races.xml @@ -701,7 +701,7 @@ - + diff --git a/res/eressea/spells.xml b/res/eressea/spells.xml index 1d762f4bd..6b2e43a15 100644 --- a/res/eressea/spells.xml +++ b/res/eressea/spells.xml @@ -10,14 +10,12 @@ - - @@ -494,12 +492,10 @@ - - @@ -507,7 +503,6 @@ - @@ -520,12 +515,10 @@ - - @@ -533,18 +526,15 @@ - - - @@ -558,12 +548,10 @@ - - @@ -583,25 +571,21 @@ - - - - @@ -609,7 +593,6 @@ - @@ -617,20 +600,17 @@ - - - diff --git a/res/eressea/strings.xml b/res/eressea/strings.xml index f7497b810..8afeabbca 100644 --- a/res/eressea/strings.xml +++ b/res/eressea/strings.xml @@ -116,18 +116,6 @@ Eine Geburtstagstorte mit 10 Kerzen. Herzlichen Glückwunsch, Eressea! A birthday cake with 10 candles. Happy Birthday, Eressea! - - Dieses Fluggerät aus der Schmiede der Zwerge von Celeband galt wie die - 'Ebene der Herausforderung' seit Urzeiten als verschollen, ja man - zweifelte seine Existenz an. Die Sage überliefert, das derjenige, der - sie auf der Spitze des Turmes seiner Gesinnung benutzt, als einziger - die 'Ebene der Herausforderungen' verlassen kann. - - - Glückwunsch, mein Kind. Du bist im Besitz des mächtigsten - Artefaktes Eresseas. Ein Fluch, sagt man, liege auf ihm, denn - niemand hat es bisher lange sein Eigen genannt... - Kleines trockenes Dauergebäck, m od. s; - u. -es, - u. -e @@ -374,29 +362,6 @@ - - - Auge des Dämon - eye of the demon - oeil du démon - - - Augen des Dämon - eyes of the demon - oeil du démon - - - Schwinge des Greifen - wing of the gryphon - aile du griffon - - - Schwingen des Greifen - wings of the gryphon - ailes du griffon - - - Adamantium adamantium @@ -427,5 +392,14 @@ Adamantiumrüstungen adamantium plates - + + Katzenamulett + amulet of the kitten + amulette du chaton + + + amulets of the kitten + Katzenamulette + amulettes du chaton + diff --git a/res/races.xml b/res/races.xml deleted file mode 100644 index fcfc1742b..000000000 --- a/res/races.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/res/races/goblin-2.xml b/res/races/goblin-2.xml deleted file mode 100644 index f26fa780e..000000000 --- a/res/races/goblin-2.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/res/races/goblin-3.xml b/res/races/goblin-3.xml index 3fd594939..a65d4c99a 100644 --- a/res/races/goblin-3.xml +++ b/res/races/goblin-3.xml @@ -1,5 +1,5 @@ - =0 - if r.morale>=0 then - create_curse(mage, r, "blessedharvest", force, force*2, 50 * force) - end - end - end - return level -end diff --git a/scripts/eressea/frost.lua b/scripts/eressea/frost.lua index 2c2df4dae..beeef68f6 100644 --- a/scripts/eressea/frost.lua +++ b/scripts/eressea/frost.lua @@ -1,11 +1,11 @@ local function is_winter(turn) local season = get_season(turn) - return season == "calendar::winter" + return season == "winter" end local function is_spring(turn) local season = get_season(turn) - return season == "calendar::spring" + return season == "spring" end local function freeze(r, chance) diff --git a/scripts/eressea/ponnuki.lua b/scripts/eressea/ponnuki.lua index 29f882ab5..1a5d1959e 100644 --- a/scripts/eressea/ponnuki.lua +++ b/scripts/eressea/ponnuki.lua @@ -38,6 +38,7 @@ function ponnuki.init() u.name = "Ponnuki" u.info = "Go, Ponnuki, Go!" u.race_name = "Ritter von Go" + u.status = 5 -- FLIEHE print(u:show()) end else diff --git a/scripts/eressea/resources.lua b/scripts/eressea/resources.lua index 2829d00c7..fa6e1c4da 100644 --- a/scripts/eressea/resources.lua +++ b/scripts/eressea/resources.lua @@ -1,16 +1,5 @@ -- global functions used in items.xml -if not item_canuse then - -- define a default, everyone can use everything - function item_canuse(u, iname) - return true - end -end - -function peasant_getresource(u) - return u.region:get_resource("peasant") -end - function peasant_changeresource(u, delta) local p = u.region:get_resource("peasant") p = p + delta @@ -21,10 +10,6 @@ function peasant_changeresource(u, delta) return p end -function hp_getresource(u) - return u.hp -end - function hp_changeresource(u, delta) local hp = u.hp + delta diff --git a/scripts/eressea/xmasitems.lua b/scripts/eressea/xmasitems.lua index a05e023b8..dffea2675 100644 --- a/scripts/eressea/xmasitems.lua +++ b/scripts/eressea/xmasitems.lua @@ -25,6 +25,7 @@ end function use_stardust(u, amount) local p = u.region:get_resource("peasant") + assert(p>0) p = math.ceil(1.5 * p) u.region:set_resource("peasant", p) local msg = usepotion_message(u, "stardust") @@ -86,7 +87,7 @@ local self = {} function self.update() local turn = get_turn() local season = get_season(turn) - if season == "calendar::winter" then + if season == "winter" then eressea.log.debug("it is " .. season .. ", the christmas trees do their magic") local msg = message.create("xmastree_effect") for r in regions() do @@ -102,7 +103,7 @@ function self.update() end else local prevseason = get_season(turn-1) - if prevseason == "calendar::winter" then + if prevseason == "winter" then -- we celebrate knut and kick out the trees. for r in regions() do if r:get_key("xm06") then diff --git a/scripts/run-tests-e2.lua b/scripts/run-tests-e2.lua index 4a2b3e59e..27617592f 100644 --- a/scripts/run-tests-e2.lua +++ b/scripts/run-tests-e2.lua @@ -17,6 +17,7 @@ require 'eressea.path' require 'tests.e2' require 'lunit' +rng.inject(0) rules = require('eressea.' .. config.rules) result = lunit.main() return result.errors + result.failed diff --git a/scripts/run-tests-e3.lua b/scripts/run-tests-e3.lua index 47bcca60b..0aefb5d3a 100644 --- a/scripts/run-tests-e3.lua +++ b/scripts/run-tests-e3.lua @@ -17,6 +17,7 @@ require 'eressea.xmlconf' require 'tests.e3' require 'lunit' +rng.inject(0) eressea.settings.set("rules.alliances", "0") rules = require('eressea.' .. config.rules) result = lunit.main() diff --git a/scripts/tests/common.lua b/scripts/tests/common.lua index a9dcf2468..936d2a79b 100644 --- a/scripts/tests/common.lua +++ b/scripts/tests/common.lua @@ -6,7 +6,7 @@ local function _test_create_ship(r) end local function create_faction(race) - return faction.create(race .. '@eressea.de', race, "de") + return faction.create(race, race .. '@eressea.de', "de") end local function one_unit(r, f) @@ -218,6 +218,9 @@ function test_gmtool() local r1 = region.create(1, 0, "plain") local r2 = region.create(1, 1, "plain") local r3 = region.create(1, 2, "plain") + if not gmtool then + return + end gmtool.open() gmtool.select(r1, true) gmtool.select_at(0, 1, true) @@ -1033,6 +1036,37 @@ function test_recruit() end end +function test_give_horses() + local r = region.create(0, 0, "plain") + local f = faction.create("human", "noreply@eressea.de", "de") + local u = unit.create(f, r, 1) + + r:set_resource("horse", 0) + u:add_item("horse", 21) + u:add_item("dolphin", 10) + u:add_order("GIB 0 7 PFERD") + u:add_order("GIB 0 5 DELPHIN") + process_orders() + assert_equal(7, r:get_resource("horse")) + assert_equal(5, u:get_item("dolphin")) + assert_equal(14, u:get_item("horse")) +end + +function test_give_silver() + local r = region.create(0, 0, "plain") + local f = faction.create("human", "noreply@eressea.de", "de") + local u = unit.create(f, r, 1) + + r:set_resource("peasant", 0) + r:set_resource("money", 11) + u:clear_orders() + u:add_item("money", 20) + u:add_order("GIB 0 10 SILBER") + process_orders() + assert_equal(21, r:get_resource("money")) + assert_equal(10, u:get_item("money")) +end + function test_give_horses() local r = region.create(0, 0, "plain") local f = create_faction('human') diff --git a/scripts/tests/config.lua b/scripts/tests/config.lua index e47b7f925..a83b64350 100644 --- a/scripts/tests/config.lua +++ b/scripts/tests/config.lua @@ -11,7 +11,7 @@ function test_read_race() eressea.free_game() assert_not_nil(eressea.config) eressea.config.parse('{ "races": { "orc" : {}}}') - f = faction.create("orc@example.com", "orc", "en") + f = faction.create("orc", "orc@example.com", "en") assert_not_nil(f) end diff --git a/scripts/tests/e2/adamantium.lua b/scripts/tests/e2/adamantium.lua index 8bbccdd19..de9a2312f 100644 --- a/scripts/tests/e2/adamantium.lua +++ b/scripts/tests/e2/adamantium.lua @@ -11,7 +11,7 @@ function setup() end local function create_faction(race) - return faction.create(race .. '@eressea.de', race, "de") + return faction.create(race, race .. '@eressea.de', "de") end local function _test_create_adamantium() diff --git a/scripts/tests/e2/buildings.lua b/scripts/tests/e2/buildings.lua new file mode 100644 index 000000000..4797643e1 --- /dev/null +++ b/scripts/tests/e2/buildings.lua @@ -0,0 +1,53 @@ +require "lunit" + +module("tests.e2.buildings", package.seeall, lunit.testcase ) + +function setup() + eressea.game.reset() + eressea.settings.set("rules.food.flags", "4") +end + +function teardown() + eressea.settings.set("rules.food.flags", "0") +end + +function test_castle_names() + local r = region.create(0, 0, "plain") + local b = building.create(r, "castle") + + assert_equal("site", b:get_typename(1)) + assert_equal("tradepost", b:get_typename(2)) + assert_equal("tradepost", b:get_typename(9)) + assert_equal("fortification", b:get_typename(10)) + assert_equal("fortification", b:get_typename(49)) + assert_equal("tower", b:get_typename(50)) + assert_equal("tower", b:get_typename(249)) + assert_equal("castle", b:get_typename(250)) + assert_equal("castle", b:get_typename(1249)) + assert_equal("fortress", b:get_typename(1250)) + assert_equal("fortress", b:get_typename(6249)) + assert_equal("citadel", b:get_typename(6250)) +end + +function test_build_castle_stages() + local r = region.create(0,0, "plain") + local f = faction.create("human") + local u = unit.create(f, r, 1000) + local b = building.create(r, "castle") + + u:add_item("stone", 1000) + + u:set_skill("building", 1) + u:clear_orders() + + u:add_order("MACHE BURG " .. itoa36(b.id)) + process_orders() + assert_equal(10, b.size) + + u:set_skill("building", 3) + u:clear_orders() + + u:add_order("MACHE BURG " .. itoa36(b.id)) + process_orders() + assert_equal(250, b.size) +end diff --git a/scripts/tests/e2/destroy.lua b/scripts/tests/e2/destroy.lua index 3f422c2e5..430ca301c 100644 --- a/scripts/tests/e2/destroy.lua +++ b/scripts/tests/e2/destroy.lua @@ -10,7 +10,7 @@ end function disabled_test_dont_move_after_destroy() local r1 = region.create(0, 0, "plain") local r2 = region.create(1, 0, "plain") - local f = faction.create("test@example.com", "human", "de") + local f = faction.create("human", "test@example.com", "de") local u = unit.create(f, r1, 1) u.building = building.create(u.region, "castle") u:clear_orders() @@ -26,8 +26,8 @@ end function test_dont_destroy_after_attack() local r1 = region.create(0, 0, "plain") - local u = unit.create(faction.create("one@example.com", "human", "de"), r1, 10) - local u2 = unit.create(faction.create("two@example.com", "human", "de"), r1, 1) + local u = unit.create(faction.create("human", "one@example.com", "de"), r1, 10) + local u2 = unit.create(faction.create("human", "two@example.com", "de"), r1, 1) u.building = building.create(u.region, "castle") u:clear_orders() u:add_order("ATTACKIERE " .. itoa36(u2.id)) @@ -38,7 +38,7 @@ end function test_destroy_is_long() local r1 = region.create(0, 0, "plain") - local u = unit.create(faction.create("one@example.com", "human", "de"), r1, 10) + local u = unit.create(faction.create("human", "one@example.com", "de"), r1, 10) u.building = building.create(u.region, "castle") u:clear_orders() u:add_order("LERNE Unterhaltung") diff --git a/scripts/tests/e2/e2features.lua b/scripts/tests/e2/e2features.lua index 5b2caf61c..1a4047d63 100644 --- a/scripts/tests/e2/e2features.lua +++ b/scripts/tests/e2/e2features.lua @@ -11,14 +11,14 @@ function setup() end function test_calendar() - assert_equal(get_season(1011), "calendar::winter") - assert_equal(get_season(1012), "calendar::spring") + assert_equal("winter", get_season(1011)) + assert_equal("spring", get_season(1012)) end function test_herbalism() -- OBS: herbalism is currently an E2-only skill local r = region.create(0, 0, "plain") - local f = faction.create("herbalism@eressea.de", "human", "de") + local f = faction.create("human", "herbalism@eressea.de", "de") local u = unit.create(f, r, 1) eressea.settings.set("rules.grow.formula", 0) -- plants do not grow @@ -44,7 +44,7 @@ function test_dwarf_bonus() r:set_resource("iron", 100) local level = r:get_resourcelevel("iron") assert_equal(1, level) - local u = unit.create(faction.create('test@example.com', "dwarf"), r) + local u = unit.create(faction.create("dwarf"), r) assert_equal("dwarf", u.faction.race) assert_equal("dwarf", u.race) u.faction.name = "Zwerge" @@ -65,7 +65,7 @@ end function test_build_harbour() -- try to reproduce mantis bug 2221 local r = region.create(0, 0, "plain") - local f = faction.create("harbour@eressea.de", "human", "de") + local f = faction.create("human", "harbour@eressea.de", "de") local u = unit.create(f, r) size = 30 u.number = 20 @@ -93,8 +93,8 @@ local function one_unit(r, f) end local function two_factions() - local f1 = faction.create("one@eressea.de", "human", "de") - local f2 = faction.create("two@eressea.de", "human", "de") + local f1 = faction.create("human", "one@eressea.de", "de") + local f2 = faction.create("human", "two@eressea.de", "de") return f1, f2 end @@ -105,7 +105,7 @@ end function test_learn() eressea.settings.set("study.random_progress", "0") local r = region.create(0, 0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") f.age = 20 local u = unit.create(f, r) u:clear_orders() @@ -124,7 +124,7 @@ end function test_teach() eressea.settings.set("study.random_progress", "0") local r = region.create(0, 0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") f.age = 20 local u = unit.create(f, r, 10) local u2 = unit.create(f, r) @@ -141,7 +141,7 @@ end function test_rename() local r = region.create(0, 0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u = unit.create(f, r) u:add_item("aoh", 1) assert_equal(u:get_item("ao_healing"), 1) @@ -149,7 +149,7 @@ end function test_unit_limit_is_1500() local r = region.create(0,0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") for i = 1,1500 do unit.create(f, r, 1) end @@ -165,7 +165,7 @@ function test_ship_capacity() local r = region.create(0,0, "ocean") region.create(1,0, "ocean") local r2 = region.create(2,0, "ocean") - local f = faction.create("capacity@eressea.de", "human", "de") + local f = faction.create("human", "capacity@eressea.de", "de") -- u1 is at the limit and moves local s1 = ship.create(r, "boat") @@ -202,7 +202,7 @@ end function test_levitate() local r = region.create(0,0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u = unit.create(f, r, 2) local s = ship.create(r, "boat") u.ship = s @@ -228,15 +228,15 @@ end function test_races() local races = { "wolf", "orc", "human", "demon" } for k,v in ipairs(races) do - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") assert_not_equal(nil, f) end end function test_can_give_person() local r = region.create(0, 0, "plain") - local f1 = faction.create("noreply@eressea.de", "human", "de") - local f2 = faction.create("noreply@eressea.de", "human", "de") + local f1 = faction.create("human", "noreply@eressea.de", "de") + local f2 = faction.create("human", "noreply@eressea.de", "de") local u1 = unit.create(f1, r, 10) local u2 = unit.create(f2, r, 10) u1.faction.age = 10 @@ -254,29 +254,10 @@ function test_can_give_person() end function test_no_uruk() - local f1 = faction.create("noreply@eressea.de", "uruk", "de") + local f1 = faction.create("uruk") assert_equal(f1.race, "orc") end -function test_snowman() - local r = region.create(0, 0, "glacier") - local f = faction.create("noreply@eressea.de", "human", "de") - local u = unit.create(f, r, 1) - u:add_item("snowman", 1) - u:clear_orders() - u:add_order("BENUTZEN 1 Schneemann") - process_orders() - for u2 in r.units do - if u2.id~=u.id then - assert_equal("snowman", u2.race) - assert_equal(1000, u2.hp) - u = nil - break - end - end - assert_equal(nil, u) -end - function test_block_movement() eressea.settings.set("rules.guard.base_stop_prob", "0.3") eressea.settings.set("rules.guard.amulet_stop_prob", "0.0") @@ -368,7 +349,7 @@ end function test_stonegolems() local r0 = region.create(0, 0, "plain") - local f1 = faction.create("noreply@eressea.de", "stonegolem", "de") + local f1 = faction.create("stonegolem") local u1 = unit.create(f1, r0, 1) local u2 = unit.create(f1, r0, 2) local c1 = building.create(r0, "castle") @@ -397,7 +378,7 @@ end function test_birthdaycake() r = region.create(0,0, "plain") - f = faction.create("cake@eressea.de", "human", "de") + f = faction.create("human") u = unit.create(f, r, 1) u:add_item("birthdaycake", 1) u:clear_orders() @@ -408,7 +389,7 @@ end function test_demonstealth() local desc, r, f, u r = region.create(0, 0, "plain") - f = faction.create("demon@eressea.de", "demon", "de") + f = faction.create("demon") u = unit.create(f, r, 1) u:clear_orders() @@ -423,3 +404,7 @@ function test_demonstealth() desc = u:show() assert_equal(nil, string.find(desc, "Drache")) end + +function test_calendar_season_2328() + assert_equal("fall", get_season(1026)) +end diff --git a/scripts/tests/e2/guard.lua b/scripts/tests/e2/guard.lua index 8e62a364d..e3cf8075f 100644 --- a/scripts/tests/e2/guard.lua +++ b/scripts/tests/e2/guard.lua @@ -11,7 +11,7 @@ end function test_guard_unarmed() local r1 = region.create(0, 0, "plain") - local f1 = faction.create("hodor@eressea.de", "human", "de") + local f1 = faction.create("human", "hodor@eressea.de", "de") local u1 = unit.create(f1, r1, 1) assert_equal(false, u1.guard) u1:clear_orders() @@ -22,7 +22,7 @@ end function test_guard_armed() local r1 = region.create(0, 0, "plain") - local f1 = faction.create("hodor@eressea.de", "human", "de") + local f1 = faction.create("human", "hodor@eressea.de", "de") local u1 = unit.create(f1, r1, 1) assert_equal(false, u1.guard) u1:add_item("sword", 1) @@ -36,10 +36,10 @@ end function test_guard_allows_move_after_combat() -- bug 1493 local r1 = region.create(0, 0, "plain") local r2 = region.create(1, 0, "plain") - local f1 = faction.create("bernd@eressea.de", "human", "de") + local f1 = faction.create("human", "bernd@eressea.de", "de") local u1 = unit.create(f1, r1, 10) local uid1 = u1.id - local f2 = faction.create("horst@eressea.de", "human", "de") + local f2 = faction.create("human", "horst@eressea.de", "de") local u2 = unit.create(f2, r1, 1) u1:add_order("BEWACHE") u1:add_item("sword", 10) @@ -59,10 +59,10 @@ end function test_no_guard_no_move_after_combat() -- bug 1493 local r1 = region.create(0, 0, "plain") local r2 = region.create(1, 0, "plain") - local f1 = faction.create("bernd@eressea.de", "human", "de") + local f1 = faction.create("human", "bernd@eressea.de", "de") local u1 = unit.create(f1, r1, 10) local uid1 = u1.id - local f2 = faction.create("horst@eressea.de", "human", "de") + local f2 = faction.create("human", "horst@eressea.de", "de") local u2 = unit.create(f2, r1, 1) u1:add_order("BEWACHE") u1:add_item("sword", 10) diff --git a/scripts/tests/e2/init.lua b/scripts/tests/e2/init.lua index 991014287..7fbc0eb95 100644 --- a/scripts/tests/e2/init.lua +++ b/scripts/tests/e2/init.lua @@ -1,3 +1,6 @@ +require 'tests.e2.spells' +require 'tests.e2.buildings' +require 'tests.e2.production' require 'tests.e2.adamantium' require 'tests.e2.undead' require 'tests.e2.shiplanding' @@ -5,8 +8,9 @@ require 'tests.e2.e2features' require 'tests.e2.movement' require 'tests.e2.destroy' require 'tests.e2.guard' -require 'tests.e2.spells' require 'tests.e2.stealth' +require 'tests.e2.items' +require 'tests.items' require 'tests.economy' require 'tests.orders' require 'tests.common' @@ -15,3 +19,5 @@ require 'tests.storage' require 'tests.magicbag' require 'tests.process' require 'tests.xmas' +require 'tests.production' +require 'tests.spells' diff --git a/scripts/tests/e2/items.lua b/scripts/tests/e2/items.lua index 9bc747650..da7b3ae8f 100644 --- a/scripts/tests/e2/items.lua +++ b/scripts/tests/e2/items.lua @@ -11,9 +11,41 @@ function setup() eressea.settings.set("magic.regeneration.enable", "0") end +function test_nestwarmth_insect() + local r = region.create(0, 0, "plain") + local f = faction.create("insect", "noreply@eressea.de", "de") + local u = unit.create(f, r, 1) + local flags = u.flags + u:add_item("nestwarmth", 2) + u:clear_orders() + u:add_order("BENUTZEN 1 Nestwaerme") + turn_begin() + turn_process() + assert_equal(flags+64, u.flags) -- UFL_WARMTH + assert_equal(1, u:get_item("nestwarmth")) + assert_equal(1, f:count_msg_type('usepotion')) + turn_end() +end + +function test_nestwarmth_other() + local r = region.create(0, 0, "plain") + local f = faction.create("human", "noreply@eressea.de", "de") + local u = unit.create(f, r, 1) + local flags = u.flags + u:add_item("nestwarmth", 2) + u:clear_orders() + u:add_order("BENUTZEN 1 Nestwaerme") + turn_begin() + turn_process() + assert_equal(flags, u.flags) -- nothing happens + assert_equal(2, u:get_item("nestwarmth")) + assert_equal(1, f:count_msg_type('error163')) + turn_end() +end + function test_meow() local r = region.create(0, 0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u = unit.create(f, r, 1) u:add_item("aoc", 1) u:clear_orders() @@ -27,7 +59,7 @@ end function test_aurapotion50() local r = region.create(0, 0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u = unit.create(f, r, 1) u:add_item("aurapotion50", 1) u:set_skill('magic', 10); @@ -45,33 +77,45 @@ end function test_bagpipe() local r = region.create(0, 0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u = unit.create(f, r, 1) + turn_begin() u:add_item("bagpipeoffear", 1) u:clear_orders() u:add_order("BENUTZEN 1 Dudelsack") - process_orders() + assert_equal(nil, r:get_curse('depression')) + turn_process() + assert_equal(0, r:get_curse('depression')) assert_equal(1, u:get_item("bagpipeoffear")) assert_equal(1, f:count_msg_type('bagpipeoffear_faction')) assert_equal(1, r:count_msg_type('bagpipeoffear_region')) + turn_end() + -- duration is variable, but at least 4 + assert_equal(0, r:get_curse('depression')) end function test_speedsail() local r = region.create(0, 0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u = unit.create(f, r, 1) + + turn_begin() u.ship = ship.create(r, "boat") u:add_item("speedsail", 2) u:clear_orders() u:add_order("BENUTZEN 1 Sonnensegel") - process_orders() + assert_equal(nil, u.ship:get_curse('shipspeed')) + turn_process() + assert_equal(1, u.ship:get_curse('shipspeed')) assert_equal(1, u:get_item("speedsail")) assert_equal(1, f:count_msg_type('use_speedsail')) + turn_end() + assert_equal(1, u.ship:get_curse('shipspeed')) -- effect stays forever end function test_foolpotion() local r = region.create(0, 0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u = unit.create(f, r, 1) turn_begin() u:add_item("p7", 1) @@ -97,7 +141,7 @@ end function test_snowman() local r = region.create(0, 0, "glacier") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u = unit.create(f, r, 1) u:add_item("snowman", 1) u:clear_orders() diff --git a/scripts/tests/e2/movement.lua b/scripts/tests/e2/movement.lua index 390af2c48..b17d369f4 100644 --- a/scripts/tests/e2/movement.lua +++ b/scripts/tests/e2/movement.lua @@ -13,8 +13,8 @@ end local r = region.create(0, 0, "plain") local r2 = region.create(1, 0, "plain") local r3 = region.create(-1, 0, "ocean") - local f = faction.create("pirate@eressea.de", "human", "de") - local f2 = faction.create("elf@eressea.de", "human", "de") + local f = faction.create("human", "pirate@eressea.de", "de") + local f2 = faction.create("human", "elf@eressea.de", "de") local u1 = unit.create(f, r2, 1) local u2 = unit.create(f2, r3, 1) @@ -39,7 +39,7 @@ end function test_dolphin_on_land() local r1 = region.create(0, 0, "plain") local r2 = region.create(1, 0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u1 = unit.create(f, r1, 1) u1.race = "dolphin" u1:clear_orders() @@ -51,7 +51,7 @@ end function test_dolphin_to_land() local r1 = region.create(0, 0, "ocean") local r2 = region.create(1, 0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u1 = unit.create(f, r1, 1) u1.race = "dolphin" u1:clear_orders() @@ -63,7 +63,7 @@ end function test_dolphin_in_ocean() local r1 = region.create(0, 0, "ocean") local r2 = region.create(1, 0, "ocean") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u1 = unit.create(f, r1, 1) u1.race = "dolphin" u1:clear_orders() @@ -75,7 +75,7 @@ end function test_follow() local r1 = region.create(0, 0, "plain") local r2 = region.create(1, 0, "plain") - local f = faction.create("test@example.com", "human", "de") + local f = faction.create("human", "test@example.com", "de") local u1 = unit.create(f, r1, 1) local u2 = unit.create(f, r1, 1) u1:clear_orders() @@ -93,7 +93,7 @@ function test_follow_ship() local r1 = region.create(0, 0, "plain") region.create(1, 0, "ocean") region.create(2, 0, "ocean") - local f = faction.create("test@example.com", "human", "de") + local f = faction.create("human", "test@example.com", "de") local u1 = unit.create(f, r1, 1) local u2 = unit.create(f, r1, 1) u1:add_item("money", 100) diff --git a/scripts/tests/e2/production.lua b/scripts/tests/e2/production.lua new file mode 100644 index 000000000..1d31011af --- /dev/null +++ b/scripts/tests/e2/production.lua @@ -0,0 +1,78 @@ +require "lunit" + +module("tests.e2.production", package.seeall, lunit.testcase ) + +function setup() + eressea.game.reset() + eressea.settings.set("rules.food.flags", "4") -- food is free + eressea.settings.set("NewbieImmunity", "0") +end + +local function create_faction(race) + return faction.create(race, race .. '@example.com', "de") +end + +function test_greatbow_needs_elf() +-- only elves can build a greatbow + local r = region.create(0, 0, 'mountain') + local f = create_faction('human') + local u = unit.create(f, r, 1) + + turn_begin() + u:set_skill('weaponsmithing', 5) + u:add_item('mallorn', 2) + u:add_order("MACHE Elfenbogen") + turn_process() -- humans cannot do it + assert_equal(1, f:count_msg_type("error117")) + assert_equal(0, u:get_item('greatbow')) + assert_equal(2, u:get_item('mallorn')) + + u.race = 'elf' + turn_process() -- but elves can + assert_equal(1, u:get_item('greatbow')) + assert_equal(0, u:get_item('mallorn')) +end + +function test_troll_quarrying_bonus() +-- Von Trollen abgebaute Steine werden nur zu 75% vom "Regionsvorrat" abgezogen. +-- Dieser Effekt ist kumulativ zu einem Steinbruch. + local r = region.create(0, 0, 'mountain') + local f = create_faction('human') + local u = unit.create(f, r, 1) + + turn_begin() + r:set_resource("stone", 100) + u:set_skill('quarrying', 4) + u:add_order("MACHE Steine") + turn_process() -- humans get no bonus + assert_equal(4, u:get_item('stone')) + assert_equal(96, r:get_resource('stone')) + + u.race = 'troll' + u:set_skill('quarrying', 2) + turn_process() -- trolls have +2 to quarrying, and save 25% + assert_equal(8, u:get_item('stone')) + assert_equal(93, r:get_resource('stone')) +end + +function test_dwarf_mining_bonus() +-- Von Zwergen abgebautes Eisen wird nur zu 60% vom "Regionsvorrat" abgezogen. +-- Dieser Effekt ist kumulativ zu einem Bergwerk (siehe hier und hier). + local r = region.create(0, 0, 'mountain') + local f = create_faction('human') + local u = unit.create(f, r, 1) + + turn_begin() + r:set_resource('iron', 100) + u:set_skill('mining', 10) + u:add_order('MACHE Eisen') + turn_process() -- humans get no bonus + assert_equal(10, u:get_item('iron')) + assert_equal(90, r:get_resource('iron')) + + u.race = 'dwarf' + u:set_skill('mining', 8) + turn_process() -- dwarves have +2 to mining, and save 40% + assert_equal(20, u:get_item('iron')) + assert_equal(84, r:get_resource('iron')) +end diff --git a/scripts/tests/e2/shiplanding.lua b/scripts/tests/e2/shiplanding.lua index a46a7da37..2258a1992 100644 --- a/scripts/tests/e2/shiplanding.lua +++ b/scripts/tests/e2/shiplanding.lua @@ -11,8 +11,8 @@ end function test_landing1() local ocean = region.create(1, 0, "ocean") local r = region.create(0, 0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") - local f2 = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") + local f2 = faction.create("human", "noreply@eressea.de", "de") local s = ship.create(ocean, "longboat") local u1 = unit.create(f, ocean, 1) local u2 = unit.create(f2, r, 1) @@ -34,8 +34,8 @@ function test_landing_harbour_with_help() local r = region.create(0, 0, "glacier") local harbour = building.create(r, "harbour") harbour.size = 25 - local f = faction.create("noreply@eressea.de", "human", "de") - local f2 = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") + local f2 = faction.create("human", "noreply@eressea.de", "de") local s = ship.create(ocean, "longboat") local u1 = unit.create(f, ocean, 1) local u2 = unit.create(f2, r, 1) @@ -60,8 +60,8 @@ function test_landing_harbour_without_help() local r = region.create(0, 0, "glacier") local harbour = building.create(r, "harbour") harbour.size = 25 - local f = faction.create("noreply@eressea.de", "human", "de") - local f2 = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") + local f2 = faction.create("human", "noreply@eressea.de", "de") local s = ship.create(ocean, "longboat") local u1 = unit.create(f, ocean, 1) local u2 = unit.create(f2, r, 1) @@ -84,7 +84,7 @@ function test_landing_harbour_unpaid() local r = region.create(0, 0, "glacier") local harbour = building.create(r, "harbour") harbour.size = 25 - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local s = ship.create(ocean, "longboat") local u1 = unit.create(f, ocean, 1) local u2 = unit.create(f, r, 1) @@ -104,8 +104,8 @@ end function test_landing_terrain() local ocean = region.create(1, 0, "ocean") local r = region.create(0, 0, "glacier") - local f = faction.create("noreply@eressea.de", "human", "de") - local f2 = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") + local f2 = faction.create("human", "noreply@eressea.de", "de") local s = ship.create(ocean, "longboat") local u1 = unit.create(f, ocean, 1) local u2 = unit.create(f2, r, 1) @@ -126,8 +126,8 @@ function test_landing_insects() local r = region.create(0, 0, "glacier") local harbour = building.create(r, "harbour") harbour.size = 25 - local f = faction.create("noreply@eressea.de", "insect", "de") - local f2 = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("insect", "noreply@eressea.de", "de") + local f2 = faction.create("human", "noreply@eressea.de", "de") local s = ship.create(ocean, "longboat") local u1 = unit.create(f, ocean, 1) local u2 = unit.create(f2, r, 1) diff --git a/scripts/tests/e2/ships.lua b/scripts/tests/e2/ships.lua index 151afc12c..351609a92 100644 --- a/scripts/tests/e2/ships.lua +++ b/scripts/tests/e2/ships.lua @@ -11,7 +11,7 @@ end function test_ship_requires_skill() local r1 = region.create(0, 0, "ocean") local r2 = region.create(1, 0, "ocean") - local f = faction.create("fake@eressea.de", "human", "de") + local f = faction.create("human", "fake@eressea.de", "de") local u1 = unit.create(f, r1, 1) u1.name = "fake" u1.ship = ship.create(r1, "longboat") @@ -25,7 +25,7 @@ end function no_test_ship_happy_case() local r1 = region.create(0, 0, "ocean") local r2 = region.create(1, 0, "ocean") - local f = faction.create("hodor@eressea.de", "human", "de") + local f = faction.create("human", "hodor@eressea.de", "de") local u1 = unit.create(f, r1, 1) local u2 = unit.create(f, r1, 1) u1.ship = ship.create(r1, "longboat") diff --git a/scripts/tests/e2/spells.lua b/scripts/tests/e2/spells.lua index 6b1123a6f..fc1e3bc3b 100644 --- a/scripts/tests/e2/spells.lua +++ b/scripts/tests/e2/spells.lua @@ -8,11 +8,12 @@ function setup() eressea.settings.set("nmr.timeout", "0") eressea.settings.set("NewbieImmunity", "0") eressea.settings.set("rules.food.flags", "4") + eressea.settings.set("rules.peasants.growth.factor", "0") end function test_shapeshift() local r = region.create(42, 0, "plain") - local f = faction.create("noreply@eressea.de", "demon", "de") + local f = faction.create("demon", "noreply@eressea.de", "de") local u1 = unit.create(f, r, 1) local u2 = unit.create(f, r, 1) u1:clear_orders() @@ -26,3 +27,78 @@ function test_shapeshift() s = u2:show() assert_equal("1 Goblin", string.sub(s, string.find(s, "1 Goblin"))) end + +function test_raindance() + local r = region.create(0, 0, "plain") + local f = faction.create("halfling", "noreply@eressea.de", "de") + local u = unit.create(f, r) + local err = 0 + r:set_resource("peasant", 100) + r:set_resource("money", 0) + u.magic = "gwyrrd" + u.race = "dwarf" + u:set_skill("magic", 20) + u.aura = 200 + err = err + u:add_spell("raindance") + assert_equal(0, err) + + u:clear_orders() + u:add_order("ZAUBERE STUFE 1 Regentanz") + assert_equal(0, r:get_resource("money")) + + process_orders() + assert_equal(200, r:get_resource("money")) + assert_equal(0, u:get_item("money")) + + u:clear_orders() + u:add_order("ARBEITEN") + process_orders() + assert_equal(10, u:get_item("money")) -- only peasants benefit + assert_equal(400, r:get_resource("money")) + -- this is where the spell ends + process_orders() + process_orders() + assert_equal(600, r:get_resource("money")) +end + +function test_earn_silver() + local r = region.create(0, 0, "mountain") + local f = faction.create("human") + local u = unit.create(f, r) + + eressea.settings.set("rules.food.flags", "4") + eressea.settings.set("magic.fumble.enable", "0") + eressea.settings.set("rules.peasants.growth", "0") + eressea.settings.set("rules.economy.repopulate_maximum", "0") + + u.magic = "gwyrrd" + u.race = "elf" + u:set_skill("magic", 10) + u.aura = 100 + local err = u:add_spell("earn_silver#gwyrrd") + assert_equal(0, err) + + u:clear_orders() + u:add_order("ZAUBERE STUFE 1 Viehheilung") + r:set_resource("money", 350) + r:set_resource("peasant", 0) + process_orders() -- get 50 silver + assert_equal(50, u:get_item("money")) + assert_equal(300, r:get_resource("money")) + + u:clear_orders() -- get 100 silver + u:add_order("ZAUBERE STUFE 2 Viehheilung") + process_orders() + assert_equal(150, u:get_item("money")) + assert_equal(200, r:get_resource("money")) + + u:clear_orders() -- get 150 silver + u:add_order("ZAUBERE STUFE 3 Viehheilung") + process_orders() + assert_equal(300, u:get_item("money")) + assert_equal(50, r:get_resource("money")) + + process_orders() -- not enough + assert_equal(350, u:get_item("money")) + assert_equal(0, r:get_resource("money")) +end diff --git a/scripts/tests/e2/stealth.lua b/scripts/tests/e2/stealth.lua index ab4320bb1..7f531776c 100644 --- a/scripts/tests/e2/stealth.lua +++ b/scripts/tests/e2/stealth.lua @@ -21,9 +21,9 @@ function setup() set_rule('rules.food.flags', '4') local r = region.create(0,0, "plain") - f = faction.create("stealthy@eressea.de", "human", "de") + f = faction.create("human", "stealthy@eressea.de", "de") u = unit.create(f, r, 1) - f = faction.create("stealth@eressea.de", "human", "de") + f = faction.create("human", "stealth@eressea.de", "de") unit.create(f, r, 1) -- TARNE PARTEI NUMMER must have a unit in the region end diff --git a/scripts/tests/e2/undead.lua b/scripts/tests/e2/undead.lua index 9f3f67f13..71c14ac36 100644 --- a/scripts/tests/e2/undead.lua +++ b/scripts/tests/e2/undead.lua @@ -8,7 +8,7 @@ end function test_undead_give_item() local r1 = region.create(0, 0, "plain") - local f1 = faction.create("hodor@eressea.de", "human", "de") + local f1 = faction.create("human", "hodor@eressea.de", "de") local u1 = unit.create(f1, r1, 1) u1.race = "undead" u1:clear_orders() @@ -20,7 +20,7 @@ end function test_undead_dont_give_person() local r1 = region.create(0, 0, "plain") - local f1 = faction.create("hodor@eressea.de", "human", "de") + local f1 = faction.create("human", "hodor@eressea.de", "de") local u1 = unit.create(f1, r1, 2) u1.race = "undead" u1:clear_orders() diff --git a/scripts/tests/e3/castles.lua b/scripts/tests/e3/buildings.lua similarity index 69% rename from scripts/tests/e3/castles.lua rename to scripts/tests/e3/buildings.lua index b16019159..709e7df74 100644 --- a/scripts/tests/e3/castles.lua +++ b/scripts/tests/e3/buildings.lua @@ -1,6 +1,6 @@ require "lunit" -module("tests.e3.castles", package.seeall, lunit.testcase ) +module("tests.e3.buildings", package.seeall, lunit.testcase ) function setup() eressea.game.reset() @@ -11,9 +11,26 @@ function teardown() eressea.settings.set("rules.food.flags", "0") end +function test_castle_names() + local r = region.create(0, 0, "plain") + local b = building.create(r, "castle") + + assert_equal("site", b:get_typename(1)) + assert_equal("site", b:get_typename(9)) + assert_equal("fortification", b:get_typename(10)) + assert_equal("fortification", b:get_typename(49)) + assert_equal("tower", b:get_typename(50)) + assert_equal("tower", b:get_typename(249)) + assert_equal("castle", b:get_typename(250)) + assert_equal("castle", b:get_typename(1249)) + assert_equal("fortress", b:get_typename(1250)) + assert_equal("fortress", b:get_typename(6249)) + assert_equal("citadel", b:get_typename(6250)) +end + function test_build_watch() local r = region.create(0, 0, "plain") - local f = faction.create("e3build@eressea.de", "human", "de") + local f = faction.create("human", "e3build@eressea.de", "de") local u = unit.create(f, r, 1) u.number = 20 @@ -33,7 +50,7 @@ function test_build_watch() assert_equal(10, u.building.size) end -function test_watch() +function test_watch_names() local r = region.create(0, 0, "plain") local b = building.create(r, "watch") @@ -46,9 +63,9 @@ end function test_small_castles() local r = region.create(0, 0, "plain") - local f1 = faction.create("noreply@eressea.de", "human", "de") + local f1 = faction.create("human", "noreply@eressea.de", "de") local u1 = unit.create(f1, r, 1) - local f2 = faction.create("noreply@eressea.de", "halfling", "de") + local f2 = faction.create("halfling", "noreply@eressea.de", "de") local u2 = unit.create(f2, r, 1) local b = building.create(r, "castle") @@ -65,7 +82,7 @@ end function test_build_normal() local r = region.create(0, 0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u = unit.create(f, r, 1) u:clear_orders() u:add_item("stone", 10) @@ -78,7 +95,7 @@ end function test_build_packice() local r = region.create(0, 0, "packice") - local f = faction.create("packice@eressea.de", "human", "de") + local f = faction.create("human", "packice@eressea.de", "de") local u = unit.create(f, r, 1) u:clear_orders() u:add_item("stone", 10) @@ -90,7 +107,7 @@ end function test_build_castle_stages() local r = region.create(0,0, "plain") - local f = faction.create("castle@eressea.de", "human", "de") + local f = faction.create("human", "castle@eressea.de", "de") local u = unit.create(f, r, 1000) local b = building.create(r, "castle") diff --git a/scripts/tests/e3/init.lua b/scripts/tests/e3/init.lua index aa253157e..fc0586f89 100644 --- a/scripts/tests/e3/init.lua +++ b/scripts/tests/e3/init.lua @@ -1,13 +1,16 @@ -require 'tests.e3.production' -require 'tests.e3.castles' +require 'tests.e3.buildings' require 'tests.e3.stealth' require 'tests.e3.spells' require 'tests.e3.rules' require 'tests.e3.parser' require 'tests.e3.morale' +require 'tests.e3.items' +require 'tests.e3.production' +require 'tests.spells' require 'tests.economy' require 'tests.orders' require 'tests.common' --- require 'tests.report' +require 'tests.items' require 'tests.magicbag' require 'tests.process' +require 'tests.production' diff --git a/scripts/tests/e3/items.lua b/scripts/tests/e3/items.lua new file mode 100644 index 000000000..d3b857381 --- /dev/null +++ b/scripts/tests/e3/items.lua @@ -0,0 +1,76 @@ +require "lunit" + +module("tests.e3.items", package.seeall, lunit.testcase ) + +function setup() + eressea.game.reset() + eressea.settings.set("rules.food.flags", "4") -- food is free + eressea.settings.set("NewbieImmunity", "0") +end + +function test_give_horses() + local r = region.create(0, 0, "plain") + local f = faction.create("human", "noreply@eressea.de", "de") + local u = unit.create(f, r, 1) + + r:set_resource("horse", 0) + u:add_item("charger", 20) + u:add_order("GIB 0 10 Streitross") + process_orders() + assert_equal(10, r:get_resource("horse")) + assert_equal(10, u:get_item("charger")) +end + +function test_goblins() + local r = region.create(0, 0, "plain") + local f1 = faction.create("goblin", "goblin@eressea.de", "de") + local f2 = faction.create("dwarf", "dwarf@eressea.de", "de") + local f3 = faction.create("elf", "elf@eressea.de", "de") + local ud = unit.create(f1, r, 1) + local uh = unit.create(f1, r, 1) + uh.race = "halfling" + local u2 = unit.create(f2, r, 1) + local u3 = unit.create(f3, r, 1) + + local restricted = { + "towershield", "rep_crossbow", "plate", "lance", + "mallornlance", "greatbow", "greataxe", "axe", "scale", + "plate", "halberd", "greatsword", "rustyhalberd" + } + for k, v in ipairs(restricted) do + ud:add_item(v, 1) + uh:add_item(v, 1) + u2:add_item(v, 1) + u3:add_item(v, 1) + end + + uh:add_order("ATTACKIERE " .. itoa36(u2.id)) + uh:add_order("ATTACKIERE " .. itoa36(u3.id)) + ud:add_order("ATTACKIERE " .. itoa36(u2.id)) + ud:add_order("ATTACKIERE " .. itoa36(u3.id)) + process_orders() +end + +function test_make_horse() + eressea.settings.set("rules.horses.growth", "0") + local r = region.create(0, 0, "plain") + local f = faction.create("human", "horses@eressea.de", "de") + local u = unit.create(f, r, 1) + u:set_skill("training", 4) + r:set_resource("horse", 100) + u:add_order("MACHE 1 PFERD") + process_orders() + assert_equal(1, u:get_item("horse")) + assert_equal(99, r:get_resource("horse")) + + u:clear_orders() + u:add_order("MACHE 1 STREITROSS") + u:add_item("money", 200) + u:add_item("iron", 1) + process_orders() + assert_equal(1, u:get_item("charger")) + assert_equal(0, u:get_item("horse")) + assert_equal(0, u:get_item("iron")) + assert_equal(0, u:get_item("money")) + assert_equal(99, r:get_resource("horse")) +end diff --git a/scripts/tests/e3/morale.lua b/scripts/tests/e3/morale.lua index c01fad186..1dd92a758 100644 --- a/scripts/tests/e3/morale.lua +++ b/scripts/tests/e3/morale.lua @@ -14,7 +14,7 @@ end function test_when_owner_returns_morale_stays() local r = region.create(0, 0, "plain") assert_equal(1, r.morale) - local f1 = faction.create("owner_returns@eressea.de", "human", "de") + local f1 = faction.create("human", "owner_returns@eressea.de", "de") local u1 = unit.create(f1, r, 1) u1:add_item("money", 10000) local b = building.create(r, "castle") @@ -38,13 +38,13 @@ end function test_morale_alliance() local r = region.create(0, 0, "plain") assert_equal(1, r.morale) - local f1 = faction.create("ma1@eressea.de", "human", "de") + local f1 = faction.create("human", "ma1@eressea.de", "de") local u1 = unit.create(f1, r, 1) u1:add_item("money", 10000) - local f2 = faction.create("ma2@eressea.de", "human", "de") + local f2 = faction.create("human", "ma2@eressea.de", "de") local u2 = unit.create(f2, r, 1) u2:add_item("money", 10000) - local f3 = faction.create("ma3@eressea.de", "human", "de") + local f3 = faction.create("human", "ma3@eressea.de", "de") local u3 = unit.create(f3, r, 1) u3:add_item("money", 10000) @@ -95,9 +95,9 @@ end function test_bigger_castle_empty() local r = region.create(0, 0, "plain") assert_equal(1, r.morale) - local f1 = faction.create("small1@eressea.de", "human", "de") + local f1 = faction.create("human", "small1@eressea.de", "de") local u1 = unit.create(f1, r, 1) - local f2 = faction.create("small2@eressea.de", "human", "de") + local f2 = faction.create("human", "small2@eressea.de", "de") local u2 = unit.create(f2, r, 1) u1:add_item("money", 10000) @@ -129,7 +129,7 @@ end function test_morale_change() local r = region.create(0, 0, "plain") assert_equal(1, r.morale) - local f1 = faction.create("mchange@eressea.de", "human", "de") + local f1 = faction.create("human", "mchange@eressea.de", "de") local u1 = unit.create(f1, r, 1) u1:add_item("money", 10000) @@ -195,9 +195,9 @@ end function test_morale_give_command() local r = region.create(0, 0, "plain") assert_equal(1, r.morale) - local f1 = faction.create("mold1@eressea.de", "human", "de") + local f1 = faction.create("human", "mold1@eressea.de", "de") local u1 = unit.create(f1, r, 1) - local f2 = faction.create("mold2@eressea.de", "human", "de") + local f2 = faction.create("human", "mold2@eressea.de", "de") local u2 = unit.create(f2, r, 1) local b = building.create(r, "castle") diff --git a/scripts/tests/e3/parser.lua b/scripts/tests/e3/parser.lua index 9d957464d..a56f08f76 100644 --- a/scripts/tests/e3/parser.lua +++ b/scripts/tests/e3/parser.lua @@ -8,7 +8,7 @@ end function test_set_status_en() local r = region.create(0, 0, "plain") - local f = faction.create("bug_1882@eressea.de", "human", "en") + local f = faction.create("human", "bug_1882@eressea.de", "en") local u = unit.create(f, r, 1) u.status = 1 u:clear_orders() @@ -23,7 +23,7 @@ end function test_set_status_de() local r = region.create(0, 0, "plain") - local f = faction.create("bug_1882@eressea.de", "human", "de") + local f = faction.create("human", "bug_1882@eressea.de", "de") local u = unit.create(f, r, 1) u.status = 1 u:clear_orders() @@ -34,7 +34,7 @@ end function test_breed_horses() local r = region.create(0, 0, "plain") - local f = faction.create("bug_1886@eressea.de", "human", "en") + local f = faction.create("human", "bug_1886@eressea.de", "en") local u = unit.create(f, r, 1) local b = building.create(r, "stables") b.size = 10 diff --git a/scripts/tests/e3/production.lua b/scripts/tests/e3/production.lua index 85fdeb638..3469b06c4 100644 --- a/scripts/tests/e3/production.lua +++ b/scripts/tests/e3/production.lua @@ -9,171 +9,129 @@ function setup() end local function create_faction(race) - return faction.create(race .. '@eressea.de', race, "de") + return faction.create(race, race .. '@example.com', "de") end -function test_laen_needs_mine() - -- some resources require a building - -- i.e. you cannot create laen without a mine - local r = region.create(0, 0, "mountain") - local f = create_faction('human') - local u = unit.create(f, r, 1) - - turn_begin() - r:set_resource('laen', 100) - u:add_order("MACHE Laen") - u:set_skill('mining', 7) - turn_process() - assert_equal(0, u:get_item('laen')) - assert_equal(100, r:get_resource('laen')) - assert_equal(1, f:count_msg_type("error104")) -- requires building - - u.building = building.create(u.region, "mine") - u.building.working = true - u.building.size = 10 - turn_process() - assert_equal(1, u:get_item('laen')) - assert_equal(99, r:get_resource('laen')) - - turn_end() -end - -function test_mine_laen_bonus() - -- some buildings grant a bonus on the production skill - -- i.e. a mine adds +1 to mining +function test_greatbow_needs_elf() +-- only elves can build a greatbow local r = region.create(0, 0, 'mountain') local f = create_faction('human') local u = unit.create(f, r, 1) turn_begin() - r:set_resource('laen', 100) - assert_equal(100, r:get_resource('laen')) - u:add_order("MACHE Laen") - u:set_skill('mining', 6) - u.building = building.create(u.region, "mine") - u.building.working = true - u.building.size = 10 - u.number = 2 - turn_process() -- T6 is not enough for laen - assert_equal(0, u:get_item('laen')) - assert_equal(100, r:get_resource('laen')) - assert_equal(1, f:count_msg_type("manufacture_skills")) + u:set_skill('weaponsmithing', 5) + u:add_item('mallorn', 2) + u:add_order("MACHE Elfenbogen") + turn_process() -- humans cannot do it + assert_equal(1, f:count_msg_type("error117")) + assert_equal(0, u:get_item('greatbow')) + assert_equal(2, u:get_item('mallorn')) - u:set_skill('mining', 13) - turn_process() -- T13 is enough, the +1 produces one extra Laen - assert_equal(4, u:get_item('laen')) -- FAIL (3) - assert_equal(96, r:get_resource('laen')) - - turn_end() + u.race = 'elf' + turn_process() -- but elves can + assert_equal(1, u:get_item('greatbow')) + assert_equal(0, u:get_item('mallorn')) end -function test_mine_iron_bonus() - -- some buildings grant a bonus on the production skill - -- i.e. a mine adds +1 to mining iron - -- +function test_troll_no_quarrying_bonus() +-- Trolle kriegen keinen Rohstoffbonus wie in E2 local r = region.create(0, 0, 'mountain') - local f = create_faction('human') + local f = create_faction('troll') + local u = unit.create(f, r, 1) + + turn_begin() + r:set_resource("stone", 100) + u:set_skill('quarrying', 2) -- +2 Rassenbonus + u:add_order("MACHE Steine") + turn_process() + assert_equal(4, u:get_item('stone')) + assert_equal(96, r:get_resource('stone')) +end + +function test_dwarf_no_mining_bonus() +-- E3: Zwerge verlieren den Eisenabbaubonus + local r = region.create(0, 0, 'mountain') + local f = create_faction('dwarf') local u = unit.create(f, r, 1) turn_begin() r:set_resource('iron', 100) - assert_equal(100, r:get_resource('iron')) - u:add_order("MACHE Eisen") - u:set_skill('mining', 1) - u.building = building.create(u.region, "mine") - u.building.working = false - u.building.size = 10 - u.number = 2 - turn_process() -- iron can be made without a working mine + u:set_skill('mining', 8) -- +2 skill bonus + u:add_order('MACHE Eisen') + turn_process() + assert_equal(10, u:get_item('iron')) + assert_equal(90, r:get_resource('iron')) +end + +function test_dwarf_towershield() +-- Zwerge können als einzige Rasse Turmschilde, Schuppenpanzer +-- und Repetierarmbrüste bauen. + local r = region.create(0, 0, 'plain') + local f = create_faction('human') + local u = unit.create(f, r, 1) + + turn_begin() + u:set_skill('armorer', 4) + u:add_item('iron', 1) + u:add_order("MACHE Turmschild") + turn_process() -- humans cannot do it + assert_equal(1, f:count_msg_type("error117")) + assert_equal(0, u:get_item('towershield')) + assert_equal(1, u:get_item('iron')) + + u.race = 'dwarf' + u:set_skill('armorer', 2) -- dwarf has bonus +2 + turn_process() -- but dwarves can + assert_equal(1, u:get_item('towershield')) + assert_equal(0, u:get_item('iron')) + +end + +function test_dwarf_scale() +-- Zwerge können als einzige Rasse Turmschilde, Schuppenpanzer +-- und Repetierarmbrüste bauen. + local r = region.create(0, 0, 'plain') + local f = create_faction('human') + local u = unit.create(f, r, 1) + + turn_begin() + u:set_skill('armorer', 5) + u:add_item('iron', 2) + u:add_order("MACHE Schuppenpanzer") + turn_process() -- humans cannot do it + assert_equal(1, f:count_msg_type("error117")) + assert_equal(0, u:get_item('scale')) assert_equal(2, u:get_item('iron')) - assert_equal(98, r:get_resource('iron')) - u.building.working = true - turn_process() - assert_equal(6, u:get_item('iron')) - assert_equal(96, r:get_resource('iron')) - - turn_end() + u.race = 'dwarf' + u:set_skill('armorer', 3) -- dwarf has bonus +2 + turn_process() -- but dwarves can + assert_equal(1, u:get_item('scale')) + assert_equal(0, u:get_item('iron')) end -function test_quarry_bonus() - -- a quarry grants +1 to quarrying, and saves 50% stone - -- - local r = region.create(0, 0, 'mountain') +function test_dwarf_rep_xbow() +-- Zwerge können als einzige Rasse Turmschilde, Schuppenpanzer +-- und Repetierarmbrüste bauen. + local r = region.create(0, 0, 'plain') local f = create_faction('human') local u = unit.create(f, r, 1) turn_begin() - r:set_resource('stone', 100) - assert_equal(100, r:get_resource('stone')) - u:add_order("MACHE Stein") - u:set_skill('quarrying', 1) - u.number = 2 - u.building = building.create(u.region, 'quarry') - u.building.working = false - u.building.size = 10 - turn_process() - assert_equal(2, u:get_item('stone')) - assert_equal(98, r:get_resource('stone')) + u:set_skill('weaponsmithing', 5) + u:add_item('iron', 1) + u:add_item('log', 1) + u:add_order("MACHE Repetierarmbrust") + turn_process() -- humans cannot do it + assert_equal(1, f:count_msg_type("error117")) + assert_equal(0, u:get_item('rep_crossbow')) + assert_equal(1, u:get_item('iron')) + assert_equal(1, u:get_item('log')) - u.building.working = true - turn_process() - assert_equal(6, u:get_item('stone')) - assert_equal(96, r:get_resource('stone')) - - turn_end() -end - -function test_smithy_bonus_iron() --- a smithy adds +1 to weaponsmithing, and saves 50% iron - local r = region.create(0, 0, 'mountain') - local f = create_faction('human') - local u = unit.create(f, r, 1) - - turn_begin() - u.building = building.create(u.region, 'smithy') - u.building.working = false - u.building.size = 10 - u:set_skill('weaponsmithing', 5) -- needs 3 - u:add_item('iron', 100) - u:add_order("MACHE Schwert") - turn_process() -- building disabled - assert_equal(1, u:get_item('sword')) - assert_equal(99, u:get_item('iron')) - - u.building.working = true - turn_process() -- building active - assert_equal(3, u:get_item('sword')) - assert_equal(98, u:get_item('iron')) - - turn_end() -end - -function test_smithy_bonus_mixed() --- a smithy adds +1 to weaponsmithing, and saves 50% iron --- it does not save any other resource, though. - local r = region.create(0, 0, 'mountain') - local f = create_faction('human') - local u = unit.create(f, r, 1) - - turn_begin() - u.building = building.create(u.region, 'smithy') - u.building.working = false - u.building.size = 10 - u:set_skill('weaponsmithing', 5) -- needs 3 - u:add_item('iron', 100) - u:add_item('log', 100) - u:add_order("MACHE Kriegsaxt") - turn_process() -- building disabled - assert_equal(1, u:get_item('axe')) - assert_equal(99, u:get_item('iron')) - assert_equal(99, u:get_item('log')) - - u.building.working = true - turn_process() -- building active - assert_equal(3, u:get_item('axe')) - assert_equal(98, u:get_item('iron')) - assert_equal(97, u:get_item('log')) - - turn_end() + u.race = 'dwarf' + u:set_skill('weaponsmithing', 3) -- dwarf has bonus +2 + turn_process() -- but dwarves can + assert_equal(1, u:get_item('rep_crossbow')) + assert_equal(0, u:get_item('iron')) + assert_equal(0, u:get_item('log')) end diff --git a/scripts/tests/e3/rules.lua b/scripts/tests/e3/rules.lua index 3a5cdadfa..7eb2f13a3 100644 --- a/scripts/tests/e3/rules.lua +++ b/scripts/tests/e3/rules.lua @@ -38,12 +38,12 @@ function teardown() end function test_calendar() - assert_equal(get_season(396), "calendar::winter") + assert_equal("winter", get_season(396)) end function disable_test_bug_1738_build_castle_e3() local r = region.create(0, 0, "plain") - local f = faction.create("bug_1738@eressea.de", "human", "de") + local f = faction.create("human", "bug_1738@eressea.de", "de") local c = building.create(r, "castle") c.size = 228 @@ -73,10 +73,10 @@ end function disable_test_alliance() local r = region.create(0, 0, "plain") - local f1 = faction.create("noreply@eressea.de", "human", "de") + local f1 = faction.create("human", "noreply@eressea.de", "de") local u1 = unit.create(f1, r, 1) u1:add_item("money", u1.number * 100) - local f2 = faction.create("info@eressea.de", "human", "de") + local f2 = faction.create("human", "info@eressea.de", "de") local u2 = unit.create(f2, r, 1) u2:add_item("money", u2.number * 100) assert(f1.alliance==nil) @@ -125,7 +125,7 @@ end function test_no_stealth() local r = region.create(0,0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u = unit.create(f, r, 1) u:set_skill("stealth", 1) @@ -138,7 +138,7 @@ end function test_no_teach() local r = region.create(0,0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u1 = unit.create(f, r, 1) local u2 = unit.create(f, r, 1) @@ -160,7 +160,7 @@ function test_seecast() for i = 1,10 do region.create(i, 0, "ocean") end - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local s1 = ship.create(r, "cutter") local u1 = unit.create(f, r, 2) u1:set_skill("sailing", 3) @@ -193,7 +193,7 @@ function test_fishing() eressea.settings.set("rules.food.flags", "0") local r = region.create(0,0, "ocean") local r2 = region.create(1,0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local s1 = ship.create(r, "cutter") local u1 = unit.create(f, r, 3) u1.ship = s1 @@ -219,8 +219,8 @@ function test_ship_capacity() local r = region.create(0,0, "ocean") region.create(1,0, "ocean") local r2 = region.create(2,0, "ocean") - local f = faction.create("noreply@eressea.de", "human", "de") - local f2 = faction.create("noreply@eressea.de", "goblin", "de") + local f = faction.create("human", "noreply@eressea.de", "de") + local f2 = faction.create("goblin", "noreply@eressea.de", "de") -- u1 is at the limit and moves local s1 = ship.create(r, "cutter") @@ -273,9 +273,9 @@ end function test_owners() local r = region.create(0, 0, "plain") - local f1 = faction.create("noreply@eressea.de", "human", "de") + local f1 = faction.create("human", "noreply@eressea.de", "de") local u1 = unit.create(f1, r, 1) - local f2 = faction.create("noreply@eressea.de", "human", "de") + local f2 = faction.create("human", "noreply@eressea.de", "de") local u2 = unit.create(f2, r, 1) local u3 = unit.create(f2, r, 1) @@ -304,7 +304,7 @@ function test_taxes() local r = region.create(0, 0, "plain") r:set_resource("peasant", 1000) r:set_resource("money", 5000) - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u = unit.create(f, r, 1) u:clear_orders() u:add_order("LERNE Holzfaellen") -- do not work @@ -320,7 +320,7 @@ end function test_region_owner_cannot_leave_castle() local r = region.create(0, 0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") f.id = 42 local b1 = building.create(r, "castle") b1.size = 10 @@ -366,7 +366,7 @@ function market_fixture() b.size = 10 b.working = true - local f = faction.create("market1@eressea.de", "human", "de") + local f = faction.create("human", "market1@eressea.de", "de") f.id = 42 local u = unit.create(f, r, 1) u.building = b @@ -424,7 +424,7 @@ end function test_multiple_markets() local r, u1, b, herbnames, luxurynames, herbtable, luxurytable = market_fixture() local r2 = get_region(1,0) - local f = faction.create("multim@eressea.de", "human", "de") + local f = faction.create("human", "multim@eressea.de", "de") local u2 = unit.create(f, r2, 1) local b2 = building.create(r2, "market") b2.size = 10 @@ -444,7 +444,7 @@ end function test_market() local r = region.create(0, 0, "plain") - local f1 = faction.create("market2@eressea.de", "human", "de") + local f1 = faction.create("human", "market2@eressea.de", "de") local u1 = unit.create(f1, r, 1) local b = building.create(r, "market") @@ -541,7 +541,7 @@ function test_market_gives_items() r = get_region(0, 0) local b = building.create(r, "market") b.size = 10 - local f = faction.create("market0@eressea.de", "human", "de") + local f = faction.create("human", "market0@eressea.de", "de") f.id = 42 local u = unit.create(f, r, 1) u.building = b @@ -559,7 +559,7 @@ end function test_spells() local r = region.create(0, 0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u = unit.create(f, r, 1) u.race = "elf" u:clear_orders() @@ -587,7 +587,7 @@ function test_spells() end function test_canoe_passes_through_land() - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local src = region.create(0, 0, "ocean") local land = region.create(1, 0, "plain") region.create(2, 0, "ocean") @@ -610,8 +610,8 @@ end function test_give_50_percent_of_money() local r = region.create(0, 0, "plain") - local u1 = unit.create(faction.create("noreply@eressea.de", "human", "de"), r, 1) - local u2 = unit.create(faction.create("noreply@eressea.de", "orc", "de"), r, 1) + local u1 = unit.create(faction.create("human", "noreply@eressea.de", "de"), r, 1) + local u2 = unit.create(faction.create("orc", "noreply@eressea.de", "de"), r, 1) u1.faction.age = 10 u2.faction.age = 10 u1:add_item("money", 500) @@ -630,19 +630,15 @@ function test_give_50_percent_of_money() u1:add_order("GIB " .. itoa36(u2.id) .. " 221 Silber") u2:clear_orders() u2:add_order("HELFEN " .. itoa36(u1.faction.id) .. " GIB") - u2:add_item("horse", 100) - u2:add_order("GIB 0 ALLES PFERD") - local h = r:get_resource("horse") process_orders() - assert_true(r:get_resource("horse")>=h+100) assert_equal(m1-221, u1:get_item("money")) assert_equal(m2+110, u2:get_item("money")) end function test_give_100_percent_of_items() r = region.create(0, 0, "plain") - local u1 = unit.create(faction.create("noreply@eressea.de", "human", "de"), r, 1) - local u2 = unit.create(faction.create("noreply@eressea.de", "orc", "de"), r, 1) + local u1 = unit.create(faction.create("human", "noreply@eressea.de", "de"), r, 1) + local u2 = unit.create(faction.create("orc", "noreply@eressea.de", "de"), r, 1) u1.faction.age = 10 u2.faction.age = 10 u1:add_item("money", 500) @@ -660,8 +656,8 @@ end function test_cannot_give_person() local r = region.create(0, 0, "plain") - local f1 = faction.create("noreply@eressea.de", "human", "de") - local f2 = faction.create("noreply@eressea.de", "human", "de") + local f1 = faction.create("human", "noreply@eressea.de", "de") + local f2 = faction.create("human", "noreply@eressea.de", "de") local u1 = unit.create(f1, r, 10) local u2 = unit.create(f2, r, 10) u1.faction.age = 10 @@ -679,8 +675,8 @@ end function test_cannot_give_unit() local r = region.create(0, 0, "plain") - local f1 = faction.create("noreply@eressea.de", "human", "de") - local f2 = faction.create("noreply@eressea.de", "human", "de") + local f1 = faction.create("human", "noreply@eressea.de", "de") + local f2 = faction.create("human", "noreply@eressea.de", "de") local u1 = unit.create(f1, r, 10) local u2 = unit.create(f2, r, 10) u1.faction.age = 10 @@ -698,9 +694,9 @@ end function test_guard_by_owners() -- http://bugs.eressea.de/view.php?id=1756 local r = region.create(0,0, "mountain") - local f1 = faction.create("noreply@eressea.de", "human", "de") + local f1 = faction.create("human", "noreply@eressea.de", "de") f1.age=20 - local f2 = faction.create("noreply@eressea.de", "human", "de") + local f2 = faction.create("human", "noreply@eressea.de", "de") f2.age=20 local u1 = unit.create(f1, r, 1) local b = building.create(r, "castle") @@ -721,7 +717,7 @@ function test_guard_by_owners() end local function setup_packice(x, onfoot) - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local plain = region.create(0,0, "plain") local ice = region.create(1,0, "packice") local ocean = region.create(2,0, "ocean") @@ -782,7 +778,7 @@ function test_cannot_walk_into_ocean() end function test_p2() - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local r = region.create(0, 0, "plain") local u = unit.create(f, r, 1) r:set_resource("tree", 0) @@ -799,7 +795,7 @@ end function test_p2_move() -- http://bugs.eressea.de/view.php?id=1855 - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local r = region.create(0, 0, "plain") region.create(1, 0, "plain") local u = unit.create(f, r, 1) @@ -818,7 +814,7 @@ end function test_golem_use_four_iron() local r0 = region.create(0, 0, "plain") - local f1 = faction.create("noreply@eressea.de", "halfling", "de") + local f1 = faction.create("halfling", "noreply@eressea.de", "de") local u1 = unit.create(f1, r0, 3) u1.race = "irongolem" u1:set_skill("weaponsmithing", 1) @@ -828,15 +824,15 @@ function test_golem_use_four_iron() process_orders() - assert_equal(2, u1.number) assert_equal(4, u1:get_item("towershield")) + assert_equal(2, u1.number) end function test_silver_weight_stops_movement() local r1 = region.create(1, 1, "plain") local r2 = region.create(2, 1, "plain") region.create(3, 1, "plain") - local f1 = faction.create("noreply@eressea.de", "human", "de") + local f1 = faction.create("human", "noreply@eressea.de", "de") local u1 = unit.create(f1, r1, 1) u1:clear_orders() u1:add_order("NACH OST") @@ -853,7 +849,7 @@ function test_silver_weight_stops_ship() local r1 = region.create(1, 1, "ocean") local r2 = region.create(2, 1, "ocean") region.create(3, 1, "ocean") - local f1 = faction.create("noreply@eressea.de", "human", "de") + local f1 = faction.create("human", "noreply@eressea.de", "de") local u1 = unit.create(f1, r1, 1) u1:set_skill("sailing", 3) local s1 = ship.create(r1, "canoe") @@ -870,7 +866,7 @@ end function test_building_owner_can_enter_ship() local r1 = region.create(1, 2, "plain") - local f1 = faction.create("noreply@eressea.de", "human", "de") + local f1 = faction.create("human", "noreply@eressea.de", "de") local b1 = building.create(r1, "castle") b1.size = 10 local s1 = ship.create(r1, "cutter") @@ -893,7 +889,7 @@ end function test_only_building_owner_can_set_not_paid() local r = region.create(0, 0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u1 = unit.create(f, r, 1) local u2 = unit.create(f, r, 1) local mine = building.create(r, "mine") @@ -917,7 +913,7 @@ end function test_spyreport_message() local r1 = region.create(1, 2, "plain") - local f1 = faction.create("noreply@eressea.de", "human", "de") + local f1 = faction.create("human", "noreply@eressea.de", "de") local u1 = unit.create(f1, r1, 1) local u2 = unit.create(f1, r1, 1) msg = message.create("spyreport") @@ -944,7 +940,7 @@ function test_bug2083() r:set_resource("peasant", 2000) r.luxury = "balm" - local f = faction.create("2083@eressea.de", "human", "de") + local f = faction.create("human", "2083@eressea.de", "de") local u = unit.create(f, r, 1) u:set_skill("building", 8) u:add_item("stone", 100) @@ -971,7 +967,7 @@ function test_bug2083() end function test_no_uruk() - local f1 = faction.create("noreply@eressea.de", "uruk", "de") + local f1 = faction.create("uruk", "noreply@eressea.de", "de") assert_equal(f1.race, "orc") end @@ -979,7 +975,7 @@ function test_bug2187() set_rule("rules.food.flags", "0") local r = region.create(0,0,"plain") - local f = faction.create("2187@eressea.de", "goblin", "de") + local f = faction.create("goblin", "2187@eressea.de", "de") local u = unit.create(f, r, 1) u.race = "demon" u.hp = u.hp_max * u.number diff --git a/scripts/tests/e3/spells-e2.lua b/scripts/tests/e3/spells-e2.lua index bf3fa1d6c..f232b8a1a 100644 --- a/scripts/tests/e3/spells-e2.lua +++ b/scripts/tests/e3/spells-e2.lua @@ -11,7 +11,7 @@ function setup() eressea.settings.set("rules.food.flags", "4") r = region.create(0, 0, "plain") - f = faction.create("spell_payment@eressea.de", "elf", "de") + f = faction.create("elf", "spell_payment@eressea.de", "de") u = unit.create(f, r, 1) u.magic = "gray" u:set_skill("magic", 12) diff --git a/scripts/tests/e3/spells.lua b/scripts/tests/e3/spells.lua index f99e4687b..24f97c221 100644 --- a/scripts/tests/e3/spells.lua +++ b/scripts/tests/e3/spells.lua @@ -9,44 +9,37 @@ function setup() eressea.settings.set("rules.peasants.growth", "0") end -function test_blessedharvest_lasts_n_turn() +function test_raindance() local r = region.create(0, 0, "plain") - local f = faction.create("noreply@eressea.de", "halfling", "de") + local f = faction.create("halfling", "noreply@eressea.de", "de") local u = unit.create(f, r) - local err = 0 r:set_resource("peasant", 100) r:set_resource("money", 0) - u:add_item("money", 1000) u.magic = "gwyrrd" u.race = "dwarf" u:set_skill("magic", 20) u.aura = 200 - err = err + u:add_spell("raindance") - err = err + u:add_spell("blessedharvest") + local err = u:add_spell("raindance") assert_equal(0, err) u:clear_orders() u:add_order("ZAUBERE STUFE 3 Regentanz") assert_equal(0, r:get_resource("money"), 0) - local m = 0 - local p = 100 - process_orders() assert_equal(200, r:get_resource("money")) u:clear_orders() u:add_order("ARBEITEN") process_orders() process_orders() + assert_equal(500, r:get_resource("money")) process_orders() - assert_equal(800, r:get_resource("money")) - process_orders() - assert_equal(900, r:get_resource("money")) + assert_equal(600, r:get_resource("money")) end function test_magic() local r = region.create(0, 0, "plain") - local f = faction.create("noreply@eressea.de", "halfling", "de") + local f = faction.create("halfling", "noreply@eressea.de", "de") local u = unit.create(f, r) local b = building.create(r, "castle") @@ -64,3 +57,46 @@ function test_magic() -- there used to be a SEGFAULT when writing reports here: -- write_reports() end + +-- E3: earn 50 per every TWO levels of spell +function test_earn_silver() + local r = region.create(0, 0, "mountain") + local f = faction.create("human") + local u = unit.create(f, r) + + eressea.settings.set("rules.food.flags", "4") + eressea.settings.set("magic.fumble.enable", "0") + eressea.settings.set("rules.peasants.growth", "0") + eressea.settings.set("rules.economy.repopulate_maximum", "0") + + u.magic = "gwyrrd" + u.race = "elf" + u:set_skill("magic", 10) + u.aura = 100 + local err = u:add_spell("earn_silver#gwyrrd") + assert_equal(0, err) + + u:clear_orders() + u:add_order("ZAUBERE STUFE 1 Viehheilung") + r:set_resource("money", 200) + r:set_resource("peasant", 0) + process_orders() -- get 50 silver + assert_equal(50, u:get_item("money")) + assert_equal(150, r:get_resource("money")) + + u:clear_orders() -- get 50 silver + u:add_order("ZAUBERE STUFE 2 Viehheilung") + process_orders() + assert_equal(100, u:get_item("money")) + assert_equal(100, r:get_resource("money")) + + u:clear_orders() -- get 75 silver + u:add_order("ZAUBERE STUFE 3 Viehheilung") + process_orders() + assert_equal(175, u:get_item("money")) + assert_equal(25, r:get_resource("money")) + + process_orders() -- not enough + assert_equal(200, u:get_item("money")) + assert_equal(0, r:get_resource("money")) +end diff --git a/scripts/tests/e3/stealth.lua b/scripts/tests/e3/stealth.lua index 38ca7d8ea..866eb4f14 100644 --- a/scripts/tests/e3/stealth.lua +++ b/scripts/tests/e3/stealth.lua @@ -10,9 +10,9 @@ function setup() eressea.settings.set("rules.food.flags", "4") local r = region.create(0,0, "plain") - f = faction.create("stealth1@eressea.de", "human", "de") + f = faction.create("human", "stealth1@eressea.de", "de") u = unit.create(f, r, 1) - f = faction.create("stealth2@eressea.de", "human", "de") + f = faction.create("human", "stealth2@eressea.de", "de") end function test_stealth_faction_on() diff --git a/scripts/tests/faction.lua b/scripts/tests/faction.lua index 87a6b5100..c264f4596 100644 --- a/scripts/tests/faction.lua +++ b/scripts/tests/faction.lua @@ -12,7 +12,7 @@ function setup() }]] eressea.config.reset() assert(eressea.config.parse(conf)==0) - f = faction.create("faction@eressea.de", "human", "de") + f = faction.create("human", "faction@eressea.de", "de") assert(f~=nil) end diff --git a/scripts/tests/items.lua b/scripts/tests/items.lua index 48f466409..5c2093953 100644 --- a/scripts/tests/items.lua +++ b/scripts/tests/items.lua @@ -11,42 +11,59 @@ function setup() eressea.settings.set("magic.regeneration.enable", "0") end -function test_mistletoe() +function test_mistletoe_okay() local r = region.create(0, 0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u = unit.create(f, r, 1) + turn_begin() u:add_item('mistletoe', 2) u:clear_orders() u:add_order("BENUTZEN 1 Mistelzweig") - process_orders() + assert_false(u:has_attrib('fleechance')) + turn_process() + assert_true(u:has_attrib('fleechance')) assert_equal(1, u:get_item('mistletoe')) assert_equal(1, f:count_msg_type('use_item')) + turn_end() +end + +function test_mistletoe_fail() + local r = region.create(0, 0, "plain") + local f = faction.create("human", "noreply@eressea.de", "de") + local u = unit.create(f, r, 1) + turn_begin() + u:add_item('mistletoe', 1) + u:clear_orders() + u:add_order("BENUTZEN 1 Mistelzweig") + assert_false(u:has_attrib('fleechance')) u.number = 2 - process_orders() + turn_process() + assert_false(u:has_attrib('fleechance')) assert_equal(1, u:get_item('mistletoe')) assert_equal(1, f:count_msg_type('use_singleperson')) + turn_end() end function test_dreameye() local r = region.create(0, 0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u = unit.create(f, r, 1) u:add_item("dreameye", 2) u:clear_orders() u:add_order("BENUTZEN 1 Traumauge") - assert_false(u:is_cursed('skillmod')) + assert_nil(u:get_curse('skillmod')) turn_begin() turn_process() - assert_true(u:is_cursed('skillmod')) + assert_not_nil(u:get_curse('skillmod')) assert_equal(1, u:get_item("dreameye")) assert_equal(1, f:count_msg_type('use_tacticcrystal')) turn_end() - assert_false(u:is_cursed('skillmod')) + assert_nil(u:get_curse('skillmod')) end function test_manacrystal() local r = region.create(0, 0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u = unit.create(f, r, 1) u:add_item("manacrystal", 2) u:clear_orders() @@ -64,7 +81,7 @@ end function test_skillpotion() local r = region.create(0, 0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u = unit.create(f, r, 1) u:add_item("skillpotion", 2) u:clear_orders() @@ -76,31 +93,43 @@ end function test_studypotion() local r = region.create(0, 0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u = unit.create(f, r, 1) + turn_begin() u:add_item("studypotion", 2) u:clear_orders() u:add_order("LERNE Unterhaltung") u:add_order("BENUTZEN 1 Lerntrank") - process_orders() + turn_process() + -- cannot sense the "learning" attribute, because study_cmd + -- removes it during processing :( assert_equal(1, u:get_item("studypotion")) + turn_end() end function test_antimagic() local r = region.create(0, 0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u = unit.create(f, r, 1) + + turn_begin() u:add_item("antimagic", 2) u:clear_orders() u:add_order("BENUTZEN 1 Antimagiekristall") - process_orders() + assert_equal(nil, r:get_curse('antimagiczone')) + turn_process() + assert_equal(5, r:get_curse('antimagiczone')) assert_equal(1, r:count_msg_type('use_antimagiccrystal')) assert_equal(1, u:get_item("antimagic")) + turn_end() + assert_equal(5, r:get_curse('antimagiczone')) -- haelt zwei wochen + turn_end() -- hack: age the curse again + assert_equal(nil, r:get_curse('antimagiczone')) end function test_ointment() local r = region.create(0, 0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u = unit.create(f, r, 1) local hp = u.hp u.hp = 1 @@ -115,7 +144,7 @@ end function test_bloodpotion_demon() local r = region.create(0, 0, "plain") - local f = faction.create("noreply@eressea.de", "demon", "de") + local f = faction.create("demon", "noreply@eressea.de", "de") local u = unit.create(f, r, 1) u:add_item("peasantblood", 1) u:clear_orders() @@ -128,7 +157,7 @@ end function test_bloodpotion_other() local r = region.create(0, 0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u = unit.create(f, r, 1) u:add_item("peasantblood", 1) u:clear_orders() diff --git a/scripts/tests/laws.lua b/scripts/tests/laws.lua index 6f41fc54b..3a724a440 100644 --- a/scripts/tests/laws.lua +++ b/scripts/tests/laws.lua @@ -16,6 +16,7 @@ function setup() "attack" : "ATTACKIERE", "maketemp" : "MACHETEMP", "end" : "ENDE", + "use" : "BENUTZEN", "recruit" : "REKRUTIERE" } }, @@ -30,8 +31,8 @@ end function test_force_leave_on() local r = region.create(0, 0, "plain") - local f1 = faction.create("owner@eressea.de") - local f2 = faction.create("guest@eressea.de") + local f1 = faction.create("human", "owner@eressea.de") + local f2 = faction.create("human", "guest@eressea.de") local u1 = unit.create(f1, r, 1) local u2 = unit.create(f2, r, 1) local b1 = building.create(r, "castle") @@ -45,8 +46,8 @@ end function test_force_leave_off() local r = region.create(0, 0, "plain") - local f1 = faction.create("owner@eressea.de") - local f2 = faction.create("guest@eressea.de") + local f1 = faction.create("human", "owner@eressea.de") + local f2 = faction.create("human", "guest@eressea.de") local u1 = unit.create(f1, r, 1) local u2 = unit.create(f2, r, 1) local b1 = building.create(r, "castle") @@ -60,7 +61,7 @@ end function test_make_temp() local r = region.create(0, 0, "plain") - local f1 = faction.create("owner@eressea.de", "human", "de") + local f1 = faction.create("human", "owner@eressea.de", "de") local u1 = unit.create(f1, r, 10) local u, u2 @@ -86,8 +87,8 @@ end function test_force_leave_postcombat() local r = region.create(0, 0, "plain") - local f1 = faction.create("owner@eressea.de", "human", "de") - local f2 = faction.create("guest@eressea.de", "human", "de") + local f1 = faction.create("human", "owner@eressea.de", "de") + local f2 = faction.create("human", "guest@eressea.de", "de") local u1 = unit.create(f1, r, 10) local u2 = unit.create(f2, r, 10) local u, u3 diff --git a/scripts/tests/magicbag.lua b/scripts/tests/magicbag.lua index 4ca5e6ae5..b51925726 100644 --- a/scripts/tests/magicbag.lua +++ b/scripts/tests/magicbag.lua @@ -6,7 +6,7 @@ local u function setup() eressea.free_game() - u = unit.create(faction.create("test@example.com", "human", "de"), region.create(0, 0, "plain"), 1) + u = unit.create(faction.create("human", "test@example.com", "de"), region.create(0, 0, "plain"), 1) end function test_magicbag_weight() diff --git a/scripts/tests/movement.lua b/scripts/tests/movement.lua index 110b5b2a2..22bd8d30e 100644 --- a/scripts/tests/movement.lua +++ b/scripts/tests/movement.lua @@ -48,7 +48,7 @@ end function test_walk_to_land() local r1 = region.create(0, 0, "plain") local r2 = region.create(1, 0, "plain") - local f = faction.create("walk@example.com", "human", "de") + local f = faction.create("human", "walk@example.com", "de") local u = unit.create(f, r1, 1) u:add_order("NACH O") process_orders() @@ -58,7 +58,7 @@ end function test_walk_into_ocean_fails() local r1 = region.create(0, 0, "plain") local r2 = region.create(1, 0, "ocean") - local f = faction.create("test@example.com", "human", "de") + local f = faction.create("human", "test@example.com", "de") local u = unit.create(f, r1, 1) u:add_order("NACH O") process_orders() @@ -69,7 +69,7 @@ function test_walk_distance() local r1 = region.create(0, 0, "plain") local r2 = region.create(1, 0, "plain") region.create(2, 0, "plain") - local f = faction.create("test@example.com", "human", "de") + local f = faction.create("human", "test@example.com", "de") local u = unit.create(f, r1, 1) u:add_order("NACH O O") process_orders() @@ -81,7 +81,7 @@ function test_ride_max_distance() local r2 = region.create(2, 0, "plain") region.create(1, 0, "plain") region.create(3, 0, "plain") - local f = faction.create("test@example.com", "human", "de") + local f = faction.create("human", "test@example.com", "de") local u = unit.create(f, r1, 1) u:add_item("horse", 1) u:set_skill("riding", 2) @@ -94,7 +94,7 @@ function test_ride_over_capacity_leads_horse() local r1 = region.create(0, 0, "plain") local r2 = region.create(1, 0, "plain") region.create(2, 0, "plain") - local f = faction.create("test@example.com", "human", "de") + local f = faction.create("human", "test@example.com", "de") local u = unit.create(f, r1, 3) u:add_item("horse", 1) u:set_skill("riding", 2) @@ -107,7 +107,7 @@ function test_ride_no_skill_leads_horse() local r1 = region.create(0, 0, "plain") local r2 = region.create(1, 0, "plain") region.create(2, 0, "plain") - local f = faction.create("test@example.com", "human", "de") + local f = faction.create("human", "test@example.com", "de") local u = unit.create(f, r1, 1) u:add_item("horse", 1) u:add_order("NACH O O") diff --git a/scripts/tests/orders.lua b/scripts/tests/orders.lua index dde9508e9..af0919640 100644 --- a/scripts/tests/orders.lua +++ b/scripts/tests/orders.lua @@ -12,7 +12,7 @@ local r, f, u function setup() eressea.free_game() r = _G.region.create(0, 0, "mountain") - f = _G.faction.create("noreply@eressea.de", "human", "de") + f = _G.faction.create("human", "noreply@eressea.de", "de") u = _G.unit.create(f, r, 1) u:clear_orders() eressea.settings.set("rules.food.flags", "4") @@ -20,6 +20,18 @@ function setup() eressea.settings.set("NewbieImmunity", "0") end +function test_no_errors() + turn_begin() + u:add_order("!BENUTZEN 23 Yanxspirit") + turn_process() + assert_equal(0, f:count_msg_type('error43')) + u:clear_orders() + u:add_order("BENUTZEN 23 Yanxspirit") + turn_process() + assert_equal(1, f:count_msg_type('error43')) + turn_end() +end + function test_learn() u:add_order("LERNEN Hiebwaffen") _G.process_orders() diff --git a/scripts/tests/pool.lua b/scripts/tests/pool.lua index d9b336919..0d24f8129 100644 --- a/scripts/tests/pool.lua +++ b/scripts/tests/pool.lua @@ -33,7 +33,7 @@ end function test_give_nopool() local r = region.create(1, 1, "plain") - local f = faction.create("test@example.com", "human", "de") + local f = faction.create("human", "test@example.com", "de") local u1 = unit.create(f, r, 1) local u2 = unit.create(f, r, 1) u1:add_item("money", 100) @@ -45,7 +45,7 @@ end function test_give_from_faction() local r = region.create(1, 1, "plain") - local f = faction.create("test@example.com", "human", "de") + local f = faction.create("human", "test@example.com", "de") local u1 = unit.create(f, r, 1) local u2 = unit.create(f, r, 1) local u3 = unit.create(f, r, 1) @@ -62,8 +62,8 @@ function test_give_divisor() eressea.settings.set("rules.items.give_divisor", 2) eressea.settings.set("GiveRestriction", 0) local r = region.create(1, 1, "plain") - local f1 = faction.create("test@example.com", "human", "de") - local f2 = faction.create("test@example.com", "human", "de") + local f1 = faction.create("human", "test@example.com", "de") + local f2 = faction.create("human", "test@example.com", "de") local u1 = unit.create(f1, r, 1) local u2 = unit.create(f2, r, 1) u2:add_order("KONTAKTIERE " .. itoa36(u1.id)) diff --git a/scripts/tests/process.lua b/scripts/tests/process.lua index 3256c73e6..c5515be0b 100644 --- a/scripts/tests/process.lua +++ b/scripts/tests/process.lua @@ -7,7 +7,7 @@ local u, r, f,turn function setup() eressea.free_game() r = region.create(0, 0, "plain") - f = faction.create("bernd@eressea.de", "human", "de") + f = faction.create("human", "bernd@eressea.de", "de") u = unit.create(f, r, 1) u:add_item("money", 10) turn = get_turn() diff --git a/scripts/tests/production.lua b/scripts/tests/production.lua new file mode 100644 index 000000000..c8a299676 --- /dev/null +++ b/scripts/tests/production.lua @@ -0,0 +1,207 @@ +require "lunit" + +module("tests.production", package.seeall, lunit.testcase ) + +function setup() + eressea.game.reset() + eressea.settings.set("rules.food.flags", "4") -- food is free + eressea.settings.set("NewbieImmunity", "0") + eressea.settings.set("study.random_progress", "0") +end + +local function create_faction(race) + return faction.create(race, race .. '@eressea.de', "de") +end + +function test_laen_needs_mine() + -- some resources require a building + -- i.e. you cannot create laen without a mine + local r = region.create(0, 0, "mountain") + local f = create_faction('human') + local u = unit.create(f, r, 1) + + turn_begin() + r:set_resource('laen', 100) + u:add_order("MACHE Laen") + u:set_skill('mining', 7) + turn_process() + assert_equal(0, u:get_item('laen')) + assert_equal(100, r:get_resource('laen')) + assert_equal(1, f:count_msg_type("building_needed")) -- requires building + + u.building = building.create(u.region, "mine") + u.building.working = true + u.building.size = 10 + turn_process() + assert_equal(1, u:get_item('laen')) + assert_equal(99, r:get_resource('laen')) + + turn_end() +end + +function test_mine_laen_bonus() + -- some buildings grant a bonus on the production skill + -- i.e. a mine adds +1 to mining + local r = region.create(0, 0, 'mountain') + local f = create_faction('human') + local u = unit.create(f, r, 1) + + turn_begin() + r:set_resource('laen', 100) + assert_equal(100, r:get_resource('laen')) + u:add_order("MACHE Laen") + u:set_skill('mining', 6) + u.building = building.create(u.region, "mine") + u.building.working = true + u.building.size = 10 + u.number = 2 + turn_process() -- T6 is not enough for laen + assert_equal(0, u:get_item('laen')) + assert_equal(100, r:get_resource('laen')) + assert_equal(1, f:count_msg_type("manufacture_skills")) + + u:set_skill('mining', 13) + turn_process() -- T13 is enough, the +1 produces one extra Laen + assert_equal(4, u:get_item('laen')) -- FAIL (3) + assert_equal(96, r:get_resource('laen')) + + turn_end() +end + +function test_mine_iron_bonus() + -- some buildings grant a bonus on the production skill + -- i.e. a mine adds +1 to mining iron + -- + local r = region.create(0, 0, 'mountain') + local f = create_faction('human') + local u = unit.create(f, r, 1) + + turn_begin() + r:set_resource('iron', 100) + assert_equal(100, r:get_resource('iron')) + u:add_order("MACHE Eisen") + u:set_skill('mining', 1) + u.building = building.create(u.region, "mine") + u.building.working = false + u.building.size = 10 + u.number = 2 + turn_process() -- iron can be made without a working mine + assert_equal(2, u:get_item('iron')) + assert_equal(98, r:get_resource('iron')) + + u.building.working = true + turn_process() + assert_equal(6, u:get_item('iron')) + assert_equal(96, r:get_resource('iron')) + + turn_end() +end + +function test_quarry_bonus() + -- a quarry grants +1 to quarrying, and saves 50% stone + -- + local r = region.create(0, 0, 'mountain') + local f = create_faction('human') + local u = unit.create(f, r, 1) + + turn_begin() + r:set_resource('stone', 100) + assert_equal(100, r:get_resource('stone')) + u:add_order("MACHE Stein") + u:set_skill('quarrying', 1) + u.number = 2 + u.building = building.create(u.region, 'quarry') + u.building.working = false + u.building.size = 10 + turn_process() + assert_equal(2, u:get_item('stone')) + assert_equal(98, r:get_resource('stone')) + + u.building.working = true + turn_process() + assert_equal(6, u:get_item('stone')) + assert_equal(96, r:get_resource('stone')) + + turn_end() +end + +function test_smithy_no_bonus() +-- a smithy does not give a bonus to other skills +-- five cartmakers make 5 carts, no matter what + local r = region.create(0, 0, 'mountain') + local f = create_faction('human') + local u = unit.create(f, r, 1) + + turn_begin() + u.building = building.create(u.region, 'smithy') + u.building.working = false + u.building.size = 10 + u.number = 5 + u:set_skill('cartmaking', 1) -- needs 1 min + u:add_item('log', 100) + u:add_order("MACHE Wagen") + turn_process() -- building disabled + assert_equal(5, u:get_item('cart')) + assert_equal(75, u:get_item('log')) + + u.building.working = true + turn_process() -- building active + assert_equal(10, u:get_item('cart')) + assert_equal(50, u:get_item('log')) + + turn_end() +end + +function test_smithy_bonus_iron() +-- a smithy adds +1 to weaponsmithing, and saves 50% iron + local r = region.create(0, 0, 'mountain') + local f = create_faction('human') + local u = unit.create(f, r, 1) + + turn_begin() + u.building = building.create(u.region, 'smithy') + u.building.working = false + u.building.size = 10 + u:set_skill('weaponsmithing', 5) -- needs 3 + u:add_item('iron', 100) + u:add_order("MACHE Schwert") + turn_process() -- building disabled + assert_equal(1, u:get_item('sword')) + assert_equal(99, u:get_item('iron')) + + u.building.working = true + turn_process() -- building active + assert_equal(3, u:get_item('sword')) + assert_equal(98, u:get_item('iron')) + + turn_end() +end + +function test_smithy_bonus_mixed() +-- a smithy adds +1 to weaponsmithing, and saves 50% iron +-- it does not save any other resource, though. + local r = region.create(0, 0, 'mountain') + local f = create_faction('human') + local u = unit.create(f, r, 1) + + turn_begin() + u.building = building.create(u.region, 'smithy') + u.building.working = false + u.building.size = 10 + u:set_skill('weaponsmithing', 5) -- needs 3 + u:add_item('iron', 100) + u:add_item('log', 100) + u:add_order("MACHE Kriegsaxt") + turn_process() -- building disabled + assert_equal(1, u:get_item('axe')) + assert_equal(99, u:get_item('iron')) + assert_equal(99, u:get_item('log')) + + u.building.working = true + turn_process() -- building active + assert_equal(3, u:get_item('axe')) + assert_equal(98, u:get_item('iron')) + assert_equal(97, u:get_item('log')) + + turn_end() +end diff --git a/scripts/tests/report.lua b/scripts/tests/report.lua index 2a037c770..12d1538c3 100644 --- a/scripts/tests/report.lua +++ b/scripts/tests/report.lua @@ -36,7 +36,7 @@ end function test_coordinates_unnamed_plane() local p = plane.create(0, -3, -3, 7, 7) local r = region.create(0, 0, "mountain") - local f = faction.create("unnamed@eressea.de", "human", "de") + local f = faction.create("human", "unnamed@eressea.de", "de") local u = unit.create(f, r, 1) init_reports() write_report(f) @@ -46,7 +46,7 @@ end function test_coordinates_no_plane() local r = region.create(0, 0, "mountain") - local f = faction.create("noplane@eressea.de", "human", "de") + local f = faction.create("human", "noplane@eressea.de", "de") local u = unit.create(f, r, 1) init_reports() write_report(f) @@ -56,7 +56,7 @@ end function test_show_shadowmaster_attacks() local r = region.create(0, 0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u = unit.create(f, r, 1) u.race = "shadowmaster" u:clear_orders() @@ -71,7 +71,7 @@ end function test_coordinates_named_plane() local p = plane.create(0, -3, -3, 7, 7, "Hell") local r = region.create(0, 0, "mountain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u = unit.create(f, r, 1) init_reports() write_report(f) @@ -82,7 +82,7 @@ end function test_coordinates_noname_plane() local p = plane.create(0, -3, -3, 7, 7, "") local r = region.create(0, 0, "mountain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u = unit.create(f, r, 1) init_reports() write_report(f) @@ -93,7 +93,7 @@ end function test_lighthouse() eressea.free_game() local r = region.create(0, 0, "mountain") - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") region.create(1, 0, "mountain") region.create(2, 0, "ocean") region.create(0, 1, "firewall") diff --git a/scripts/tests/spells.lua b/scripts/tests/spells.lua new file mode 100644 index 000000000..e3c0a3021 --- /dev/null +++ b/scripts/tests/spells.lua @@ -0,0 +1,83 @@ +require 'lunit' +module("tests.spells", package.seeall, lunit.testcase) + +function setup() + eressea.free_game() + eressea.settings.set("nmr.removenewbie", "0") + eressea.settings.set("nmr.timeout", "0") + eressea.settings.set("NewbieImmunity", "0") + eressea.settings.set("rules.food.flags", "4") + eressea.settings.set("rules.encounters", "0") + eressea.settings.set("magic.regeneration.enable", "0") +end + +function test_create_bogus() + local r = region.create(0, 0, "plain") + local f = faction.create("human") + local u = unit.create(f, r, 1) + u.race = "elf" + u:set_skill("magic", 10) + u.magic = 'gwyrrd' + u:clear_orders() + u:add_order("ZAUBERE 'Erschaffe Katastrophe'") + process_orders() + assert_equal(f.messages[3], 'error173') -- HACKity HACK +end + +function test_create_roi() + local r = region.create(0, 0, "plain") + local f = faction.create("human") + local u = unit.create(f, r, 1) + u.race = "elf" + u:set_skill("magic", 10) + u.magic = 'gwyrrd' + u.aura = 100 + u:add_item("money", 3000) + u:add_spell("create_roi") + u:clear_orders() + u:add_order("ZAUBERE 'Erschaffe einen Ring der Unsichtbarkeit'") + local amax = u.aura_max + process_orders() + assert_equal(1, u:get_item("roi")) + assert_equal(50, u.aura) + assert_equal(amax - 1, u.aura_max) +end + +function test_create_aots() + local r = region.create(0, 0, "plain") + local f = faction.create("human") + local u = unit.create(f, r, 1) + u.race = "elf" + u:set_skill("magic", 10) + u.magic = 'gwyrrd' + u.aura = 100 + u:add_item("money", 3000) + u:add_spell("create_aots") + u:clear_orders() + u:add_order("ZAUBERE 'Erschaffe ein Amulett des wahren Sehens'") + local amax = u.aura_max + process_orders() + assert_equal(1, u:get_item("aots")) + assert_equal(50, u.aura) + assert_equal(amax - 1, u.aura_max) +end + +function test_create_dreameye() + local r = region.create(0, 0, "plain") + local f = faction.create("human") + local u = unit.create(f, r, 1) + u.race = "elf" + u:set_skill("magic", 10) + u.magic = 'gwyrrd' + u.aura = 100 + u:add_item("dragonhead", 1) + u:add_spell("create_dreameye") + u:clear_orders() + u:add_order("ZAUBERE 'Erschaffe ein Traumauge'") + local amax = u.aura_max + process_orders() + assert_equal(1, u:get_item("dreameye")) + assert_equal(100, u.aura) + assert_equal(amax - 5, u.aura_max) +end + diff --git a/scripts/tests/storage.lua b/scripts/tests/storage.lua index c262652db..32fbe9ef3 100644 --- a/scripts/tests/storage.lua +++ b/scripts/tests/storage.lua @@ -8,7 +8,7 @@ end function test_store_unit() local r = region.create(0, 0, "plain") - local f = faction.create("noreply15@eressea.de", "human", "de") + local f = faction.create("human", "noreply15@eressea.de", "de") local u = unit.create(f, r, 1) local fid = f.id u:add_item("money", u.number * 100) @@ -20,7 +20,7 @@ function test_store_unit() eressea.free_game() -- recreate world: r = region.create(0, 0, "plain") - f = faction.create("noreply16@eressea.de", "human", "de") + f = faction.create("human", "noreply16@eressea.de", "de") f.id = fid store = storage.create(filename, "rb") assert_not_nil(store) diff --git a/scripts/tests/study.lua b/scripts/tests/study.lua index 9bb8cda5e..2a713dd43 100644 --- a/scripts/tests/study.lua +++ b/scripts/tests/study.lua @@ -24,7 +24,7 @@ end function test_study() local r = region.create(0, 0, "plain") - local f = faction.create("test@example.com", "human", "de") + local f = faction.create("human", "test@example.com", "de") local u = unit.create(f, r, 1) u:add_order("LERNEN Armbrust") process_orders() @@ -33,7 +33,7 @@ end function test_study_expensive() local r = region.create(0, 0, "plain") - local f = faction.create("test@example.com", "human", "de") + local f = faction.create("human", "test@example.com", "de") local u = unit.create(f, r, 1) eressea.settings.set("skills.cost.alchemy", "50") eressea.settings.set("rules.encounters", "0") @@ -46,7 +46,7 @@ end function test_unit_spells() local r = region.create(0, 0, "plain") - local f = faction.create("test@example.com", "human", "de") + local f = faction.create("human", "test@example.com", "de") local u = unit.create(f, r, 1) u.magic = "gray" u:set_skill("magic", 1) @@ -76,7 +76,7 @@ end function test_study_no_teacher() local r = region.create(0, 0, "plain") - local f = faction.create("test@example.com", "human", "de") + local f = faction.create("human", "test@example.com", "de") local u1 = make_student(f, r, 1) u1:set_skill("crossbow", 1) process_orders() @@ -85,7 +85,7 @@ end function test_study_with_teacher() local r = region.create(0, 0, "plain") - local f = faction.create("test@example.com", "human", "de") + local f = faction.create("human", "test@example.com", "de") local u1 = make_student(f, r, 1) make_teacher(u1) @@ -96,7 +96,7 @@ end function test_study_too_many_students() local r = region.create(0, 0, "plain") - local f = faction.create("test@example.com", "human", "de") + local f = faction.create("human", "test@example.com", "de") local u1 = make_student(f, r, 20, "Taktik") u1.name = "Student" u1:add_item("money", 201*u1.number) @@ -107,7 +107,7 @@ end function test_study_multiple_teachers() local r = region.create(0, 0, "plain") - local f = faction.create("test@example.com", "human", "de") + local f = faction.create("human", "test@example.com", "de") local u1 = make_student(f, r, 20, "Taktik") u1.name = "Student" u1:add_item("money", 201*u1.number) diff --git a/scripts/tests/xmas.lua b/scripts/tests/xmas.lua index 459984901..304cc333a 100644 --- a/scripts/tests/xmas.lua +++ b/scripts/tests/xmas.lua @@ -15,7 +15,7 @@ end function test_snowglobe_fail() local r1 = region.create(0, 0, "glacier") local r2 = region.create(1, 0, "ocean") - local f = faction.create("snowglobe1@eressea.de", "human", "de") + local f = faction.create("human", "snowglobe1@eressea.de", "de") local u = unit.create(f, r1, 1) u:add_item("snowglobe", 1) u:clear_orders() @@ -30,7 +30,7 @@ end function test_snowglobe_missing_direction() local r1 = region.create(0, 0, "glacier") local r2 = region.create(1, 0, "ocean") - local f = faction.create("snowglobe1@eressea.de", "human", "de") + local f = faction.create("human", "snowglobe1@eressea.de", "de") local u = unit.create(f, r1, 1) u:add_item("snowglobe", 1) u:clear_orders() @@ -44,7 +44,7 @@ end function test_snowglobe() local r1 = region.create(0, 0, "glacier") local r2 = region.create(1, 0, "ocean") - local f = faction.create("snowglobe2@eressea.de", "human", "de") + local f = faction.create("human", "snowglobe2@eressea.de", "de") local u = unit.create(f, r1, 1) local fail = 0 u:add_item("snowglobe", have) @@ -62,7 +62,7 @@ end local function use_tree(terrain) local r = region.create(0, 0, terrain) - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u1 = unit.create(f, r, 5) r:set_resource("tree", 0) u1:add_item("xmastree", 1) @@ -86,7 +86,7 @@ function test_stardust() eressea.settings.set("rules.economy.repopulate_maximum", 0) local r = region.create(0, 0, "plain") r:set_resource("peasant", 10) - local f = faction.create("noreply@eressea.de", "human", "de") + local f = faction.create("human", "noreply@eressea.de", "de") local u = unit.create(f, r, 5) u:add_item("stardust", 1) u:clear_orders() diff --git a/share/debian-7_8.supp b/share/debian-7_8.supp index 99eefafa4..f6fabe6e7 100644 --- a/share/debian-7_8.supp +++ b/share/debian-7_8.supp @@ -50,11 +50,4 @@ obj:/usr/lib/x86_64-linux-gnu/liblua5.2.so.0.0.0 obj:/usr/lib/x86_64-linux-gnu/liblua5.2.so.0.0.0 fun:lua_isnumber - fun:class_index_event - obj:/usr/lib/x86_64-linux-gnu/liblua5.2.so.0.0.0 - obj:/usr/lib/x86_64-linux-gnu/liblua5.2.so.0.0.0 - obj:/usr/lib/x86_64-linux-gnu/liblua5.2.so.0.0.0 - obj:/usr/lib/x86_64-linux-gnu/liblua5.2.so.0.0.0 - obj:/usr/lib/x86_64-linux-gnu/liblua5.2.so.0.0.0 - obj:/usr/lib/x86_64-linux-gnu/liblua5.2.so.0.0.0 } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f9c92ff2c..c4c306118 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -187,43 +187,52 @@ target_link_libraries(eressea ${INIPARSER_LIBRARIES} ) +add_executable(convert convert.c) +target_link_libraries(convert + game + ${LUA_MATH_LIBRARY} + ${STORAGE_LIBRARIES} + ${CLIBS_LIBRARIES} + ${INIPARSER_LIBRARIES} +) + set(TESTS_SRC - monsters.test.c - names.test.c - donations.test.c - wormhole.test.c - alchemy.test.c - guard.test.c test_eressea.c tests.c + alchemy.test.c battle.test.c - vortex.test.c - tests.test.c - volcano.test.c - reports.test.c + calendar.test.c creport.test.c - report.test.c - summary.test.c - travelthru.test.c direction.test.c + donations.test.c economy.test.c + give.test.c + guard.test.c json.test.c keyword.test.c - give.test.c laws.test.c lighthouse.test.c magic.test.c market.test.c monsters.test.c move.test.c + names.test.c piracy.test.c prefix.test.c renumber.test.c + reports.test.c + report.test.c + summary.test.c skill.test.c spells.test.c spy.test.c study.test.c + tests.test.c + travelthru.test.c upkeep.test.c + volcano.test.c + vortex.test.c + wormhole.test.c spells/flyingship.test.c spells/magicresistance.test.c triggers/shock.test.c @@ -268,6 +277,7 @@ endif(CURSES_FOUND) if (LIBXML2_FOUND) include_directories (${LIBXML2_INCLUDE_DIR}) target_link_libraries(eressea ${LIBXML2_LIBRARIES}) +target_link_libraries(convert ${LIBXML2_LIBRARIES}) target_link_libraries(test_eressea ${LIBXML2_LIBRARIES}) add_definitions(-DUSE_LIBXML2) endif (LIBXML2_FOUND) diff --git a/src/attributes/CMakeLists.txt b/src/attributes/CMakeLists.txt index d0aa252ee..8be9dc7ff 100644 --- a/src/attributes/CMakeLists.txt +++ b/src/attributes/CMakeLists.txt @@ -12,7 +12,6 @@ follow.c hate.c iceberg.c key.c -matmod.c moved.c movement.c dict.c diff --git a/src/attributes/attributes.c b/src/attributes/attributes.c index 42f5247fe..f699fdb57 100644 --- a/src/attributes/attributes.c +++ b/src/attributes/attributes.c @@ -25,6 +25,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* attributes includes */ #include "follow.h" +#include "fleechance.h" #include "hate.h" #include "iceberg.h" #include "key.h" @@ -58,6 +59,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include + attrib_type at_unitdissolve = { "unitdissolve", NULL, NULL, NULL, a_writechars, a_readchars }; @@ -100,7 +102,6 @@ void register_attributes(void) at_register(&at_group); at_register(&at_building_generic_type); - at_register(&at_maxmagicians); at_register(&at_npcfaction); /* connection-typen */ @@ -112,6 +113,8 @@ void register_attributes(void) at_register(&at_germs); + at_deprecate("maxmagicians", a_readint); /* factions with differnt magician limits, probably unused */ + at_deprecate("hurting", a_readint); /* an old arena attribute */ at_deprecate("xontormiaexpress", a_readint); /* required for old datafiles */ at_deprecate("orcification", a_readint); /* required for old datafiles */ at_deprecate("lua", read_ext); /* required for old datafiles */ diff --git a/src/attributes/fleechance.c b/src/attributes/fleechance.c index afba2d301..b8012fba5 100644 --- a/src/attributes/fleechance.c +++ b/src/attributes/fleechance.c @@ -38,8 +38,3 @@ attrib *make_fleechance(float fleechance) a->data.flt = fleechance; return a; } - -void init_fleechance(void) -{ - at_register(&at_fleechance); -} diff --git a/src/attributes/fleechance.h b/src/attributes/fleechance.h index b2f75671c..d5817b8b7 100644 --- a/src/attributes/fleechance.h +++ b/src/attributes/fleechance.h @@ -25,7 +25,6 @@ extern "C" { extern struct attrib_type at_fleechance; struct attrib *make_fleechance(float fleechance); - void init_fleechance(void); #ifdef __cplusplus } diff --git a/src/attributes/matmod.c b/src/attributes/matmod.c deleted file mode 100644 index 0cf1955d8..000000000 --- a/src/attributes/matmod.c +++ /dev/null @@ -1,41 +0,0 @@ -/* -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. -**/ - -#include -#include "matmod.h" - -#include -#include - -attrib_type at_matmod = { - "matmod", - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - ATF_PRESERVE -}; - -attrib *make_matmod(mm_fun function) -{ - attrib *a = a_new(&at_matmod); - a->data.f = (void(*)(void))function; - return a; -} diff --git a/src/battle.c b/src/battle.c index 018133516..19f30e59c 100644 --- a/src/battle.c +++ b/src/battle.c @@ -576,10 +576,7 @@ static weapon *select_weapon(const troop t, bool attacking, static bool i_canuse(const unit * u, const item_type * itype) { - if (itype->canuse) { - return itype->canuse(u, itype); - } - return true; + return rc_can_use(u_race(u), itype); } static int @@ -1208,11 +1205,12 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile) if (type != AT_COMBATSPELL && type != AT_SPELL) { if (rule_damage & DAMAGE_CRITICAL) { double kritchance = (sk * 3 - sd) / 200.0; + int maxk = 4; kritchance = MAX(kritchance, 0.005); kritchance = MIN(0.9, kritchance); - while (chance(kritchance)) { + while (maxk-- && chance(kritchance)) { da += dice_rand(damage); } } @@ -1752,15 +1750,9 @@ void do_combatmagic(battle * b, combatmagic_t was) fighter *fig = co->magician.fig; const spell *sp = co->sp; - level = co->level; - if (!sp->cast) { - log_error("spell '%s' has no function.\n", sp->sname); - } - else { - level = sp->cast(co); - if (level > 0) { - pay_spell(fig->unit, sp, level, 1); - } + level = cast_spell(co); + if (level > 0) { + pay_spell(fig->unit, sp, level, 1); } } } @@ -1774,7 +1766,7 @@ static int cast_combatspell(troop at, const spell * sp, int level, double force) castorder co; create_castorder_combat(&co, at.fighter, sp, level, force); - level = sp->cast(&co); + level = cast_spell(&co); free_castorder(&co); if (level > 0) { pay_spell(at.fighter->unit, sp, level, 1); @@ -1843,12 +1835,7 @@ static void do_combatspell(troop at) return; } - if (!sp->cast) { - log_error("spell '%s' has no function.\n", sp->sname); - } - else { - level = cast_combatspell(at, sp, level, power); - } + level = cast_combatspell(at, sp, level, power); } /* Sonderattacken: Monster patzern nicht und zahlen auch keine @@ -1863,9 +1850,6 @@ static void do_extra_spell(troop at, const att * a) if (!sp) { log_error("no such spell: '%s'", a->data.sp->name); } - else if (sp->cast == NULL) { - log_error("spell '%s' has no function.", sp->sname); - } else { assert(a->level > 0); cast_combatspell(at, sp, a->level, a->level); diff --git a/src/battle.h b/src/battle.h index 85e65cf66..4e20d00d8 100644 --- a/src/battle.h +++ b/src/battle.h @@ -123,13 +123,8 @@ extern "C" { typedef struct weapon { int count, used; const struct weapon_type *type; -# ifdef LOMEM - int attackskill : 8; - int defenseskill : 8; -# else int attackskill; int defenseskill; -# endif } weapon; /*** fighter::person::flags ***/ diff --git a/src/bind_faction.c b/src/bind_faction.c index 518ccccac..f9321f6b5 100644 --- a/src/bind_faction.c +++ b/src/bind_faction.c @@ -268,6 +268,23 @@ static int tolua_faction_setkey(lua_State * L) return 0; } +static int tolua_faction_get_messages(lua_State * L) +{ + faction *self = (faction *)tolua_tousertype(L, 1, 0); + int i = 1; + mlist *ml; + if (!self->msgs) { + return 0; + } + lua_newtable(L); + for (ml = self->msgs->begin; ml; ml = ml->next, ++i) { + lua_pushnumber(L, i); + lua_pushstring(L, ml->msg->type->name); + lua_rawset(L, -3); + } + return 1; +} + static int tolua_faction_count_msg_type(lua_State *L) { faction *self = (faction *)tolua_tousertype(L, 1, 0); const char *str = tolua_tostring(L, 2, 0); @@ -401,8 +418,8 @@ static int tolua_faction_get(lua_State * L) static int tolua_faction_create(lua_State * L) { - const char *email = tolua_tostring(L, 1, 0); - const char *racename = tolua_tostring(L, 2, 0); + const char *racename = tolua_tostring(L, 1, 0); + const char *email = tolua_tostring(L, 2, 0); const char *lang = tolua_tostring(L, 3, 0); struct locale *loc = lang ? get_locale(lang) : default_locale; faction *f = NULL; @@ -637,6 +654,7 @@ void tolua_faction_open(lua_State * L) /* tech debt hack, siehe https://paper.dropbox.com/doc/Weihnachten-2015-5tOx5r1xsgGDBpb0gILrv#:h=Probleme-mit-Tests-(Nachtrag-0 */ tolua_function(L, TOLUA_CAST "count_msg_type", tolua_faction_count_msg_type); + tolua_variable(L, TOLUA_CAST "messages", tolua_faction_get_messages, NULL); tolua_function(L, TOLUA_CAST "get_key", tolua_faction_getkey); tolua_function(L, TOLUA_CAST "set_key", tolua_faction_setkey); diff --git a/src/bind_region.c b/src/bind_region.c index f0c2ba87b..2f9309549 100644 --- a/src/bind_region.c +++ b/src/bind_region.c @@ -19,6 +19,7 @@ without prior permission by the authors of Eressea. #include "chaos.h" #include +#include #include #include #include @@ -29,6 +30,7 @@ without prior permission by the authors of Eressea. #include #include #include +#include #include #include #include @@ -46,6 +48,23 @@ without prior permission by the authors of Eressea. #include #include +static int tolua_region_count_msg_type(lua_State *L) { + region *self = (region *)tolua_tousertype(L, 1, 0); + const char *str = tolua_tostring(L, 2, 0); + int n = 0; + if (self->msgs) { + mlist * ml = self->msgs->begin; + while (ml) { + if (strcmp(str, ml->msg->type->name) == 0) { + ++n; + } + ml = ml->next; + } + } + lua_pushinteger(L, n); + return 1; +} + int tolua_regionlist_next(lua_State * L) { region **region_ptr = (region **)lua_touserdata(L, lua_upvalueindex(1)); @@ -341,20 +360,16 @@ static int tolua_region_get_resourcelevel(lua_State * L) #define LUA_ASSERT(c, s) if (!(c)) { log_error("%s(%d): %s\n", __FILE__, __LINE__, (s)); return 0; } -static critbit_tree * special_resources(void) -{ - static critbit_tree cb = CRITBIT_TREE(); - if (!cb.root) { - const char * special[] = { "seed", "sapling", "tree", "grave", "chaos", 0 }; - char buffer[32]; - int i; - for (i = 0; special[i]; ++i) { - size_t len = strlen(special[i]); - len = cb_new_kv(special[i], len, &i, sizeof(int), buffer); - cb_insert(&cb, buffer, len); +static int special_resource(const char *type) { + const char * special[] = { "seed", "sapling", "tree", "grave", "chaos", 0 }; + int i; + + for (i = 0; special[i]; ++i) { + if (strcmp(type, special[i]) == 0) { + return i; } } - return &cb; + return -1; } static int tolua_region_get_resource(lua_State * L) @@ -362,32 +377,27 @@ static int tolua_region_get_resource(lua_State * L) region *r; const char *type; const resource_type *rtype; - int result = 0; - void * match; - critbit_tree * cb = special_resources(); + int result; r = (region *)tolua_tousertype(L, 1, 0); LUA_ASSERT(r != NULL, "invalid parameter"); type = tolua_tostring(L, 2, 0); LUA_ASSERT(type != NULL, "invalid parameter"); - - if (cb_find_prefix(cb, type, strlen(type) + 1, &match, 1, 0)) { - cb_get_kv(match, &result, sizeof(result)); - switch (result) { - case 0: - case 1: - case 2: - result = rtrees(r, result); - break; - case 3: - result = deathcount(r); - break; - case 4: - result = get_chaoscount(r); - break; - } - } - else { + + result = special_resource(type); + switch (result) { + case 0: + case 1: + case 2: + result = rtrees(r, result); + break; + case 3: + result = deathcount(r); + break; + case 4: + result = get_chaoscount(r); + break; + default: rtype = rt_find(type); if (rtype) { result = region_getresource(r, rtype); @@ -406,27 +416,23 @@ static int tolua_region_set_resource(lua_State * L) region *r = (region *)tolua_tousertype(L, 1, 0); const char *type = tolua_tostring(L, 2, 0); int result, value = (int)tolua_tonumber(L, 3, 0); - critbit_tree * cb = special_resources(); - void * match; + const resource_type *rtype; - if (cb_find_prefix(cb, type, strlen(type) + 1, &match, 1, 0)) { - cb_get_kv(match, &result, sizeof(result)); - switch (result) { - case 0: - case 1: - case 2: - rsettrees(r, result, value); - break; - case 3: - deathcounts(r, value - deathcount(r)); - break; - case 4: - add_chaoscount(r, value - get_chaoscount(r)); - break; - } - } - else { - const resource_type *rtype = rt_find(type); + result = special_resource(type); + switch (result) { + case 0: + case 1: + case 2: + rsettrees(r, result, value); + break; + case 3: + deathcounts(r, value - deathcount(r)); + break; + case 4: + add_chaoscount(r, value - get_chaoscount(r)); + break; + default: + rtype = rt_find(type); if (rtype != NULL) { region_setresource(r, rtype, value); } @@ -665,6 +671,27 @@ static int tolua_distance(lua_State * L) return 1; } +static int tolua_region_get_curse(lua_State *L) { + region *self = (region *)tolua_tousertype(L, 1, 0); + const char *name = tolua_tostring(L, 2, 0); + if (self->attribs) { + curse * c = get_curse(self->attribs, ct_find(name)); + if (c) { + lua_pushnumber(L, curse_geteffect(c)); + return 1; + } + } + return 0; +} + +static int tolua_region_has_attrib(lua_State *L) { + region *self = (region *)tolua_tousertype(L, 1, 0); + const char *name = tolua_tostring(L, 2, 0); + attrib * a = a_find(self->attribs, at_find(name)); + lua_pushboolean(L, a != NULL); + return 1; +} + void tolua_region_open(lua_State * L) { /* register user types */ @@ -684,6 +711,10 @@ void tolua_region_open(lua_State * L) tolua_function(L, TOLUA_CAST "destroy", tolua_region_destroy); tolua_function(L, TOLUA_CAST "__tostring", tolua_region_tostring); + tolua_function(L, TOLUA_CAST "count_msg_type", tolua_region_count_msg_type); + + tolua_function(L, TOLUA_CAST "get_curse", &tolua_region_get_curse); + tolua_function(L, TOLUA_CAST "has_attrib", &tolua_region_has_attrib); /* flags */ tolua_variable(L, TOLUA_CAST "blocked", tolua_region_get_blocked, tolua_region_set_blocked); diff --git a/src/bind_ship.c b/src/bind_ship.c index 515fa8de3..c5e0735ed 100644 --- a/src/bind_ship.c +++ b/src/bind_ship.c @@ -16,11 +16,13 @@ without prior permission by the authors of Eressea. #include "move.h" +#include #include #include #include #include +#include #include #include @@ -198,6 +200,27 @@ static int tolua_ship_set_damage(lua_State * L) return 0; } +static int tolua_ship_get_curse(lua_State *L) { + ship *self = (ship *)tolua_tousertype(L, 1, 0); + const char *name = tolua_tostring(L, 2, 0); + if (self->attribs) { + curse * c = get_curse(self->attribs, ct_find(name)); + if (c) { + lua_pushnumber(L, curse_geteffect(c)); + return 1; + } + } + return 0; +} + +static int tolua_ship_has_attrib(lua_State *L) { + ship *self = (ship *)tolua_tousertype(L, 1, 0); + const char *name = tolua_tostring(L, 2, 0); + attrib * a = a_find(self->attribs, at_find(name)); + lua_pushboolean(L, a != NULL); + return 1; +} + void tolua_ship_open(lua_State * L) { /* register user types */ @@ -225,6 +248,10 @@ void tolua_ship_open(lua_State * L) tolua_variable(L, TOLUA_CAST "type", tolua_ship_get_type, 0); tolua_variable(L, TOLUA_CAST "damage", tolua_ship_get_damage, tolua_ship_set_damage); + + tolua_function(L, TOLUA_CAST "get_curse", &tolua_ship_get_curse); + tolua_function(L, TOLUA_CAST "has_attrib", &tolua_ship_has_attrib); + tolua_function(L, TOLUA_CAST "create", tolua_ship_create); } tolua_endmodule(L); diff --git a/src/bind_unit.c b/src/bind_unit.c old mode 100755 new mode 100644 index 0358e1938..1d6579b0e --- a/src/bind_unit.c +++ b/src/bind_unit.c @@ -193,6 +193,13 @@ static int tolua_unit_set_id(lua_State * L) return 0; } +static int tolua_unit_get_auramax(lua_State * L) +{ + unit *self = (unit *)tolua_tousertype(L, 1, 0); + lua_pushinteger(L, max_spellpoints(self->region, self)); + return 1; +} + static int tolua_unit_get_hpmax(lua_State * L) { unit *self = (unit *)tolua_tousertype(L, 1, 0); @@ -512,15 +519,10 @@ static void unit_castspell(unit * u, const char *name, int level) if (sp) { spellbook *book = unit_get_spellbook(u); if (spellbook_get(book, sp)) { - if (!sp->cast) { - log_error("spell '%s' has no function.\n", sp->sname); - } - else { - castorder co; - create_castorder(&co, u, 0, sp, u->region, level, (double)level, 0, 0, 0); - sp->cast(&co); - free_castorder(&co); - } + castorder co; + create_castorder(&co, u, 0, sp, u->region, level, (double)level, 0, 0, 0); + cast_spell(&co); + free_castorder(&co); } } } @@ -771,17 +773,29 @@ static int tolua_unit_get_orders(lua_State * L) return 1; } -static int tolua_unit_is_cursed(lua_State *L) { +static int tolua_unit_get_curse(lua_State *L) { unit *self = (unit *)tolua_tousertype(L, 1, 0); const char *name = tolua_tostring(L, 2, 0); - lua_pushboolean(L, self->attribs && curse_active(get_curse(self->attribs, ct_find(name)))); - return 1; + if (self->attribs) { + curse * c = get_curse(self->attribs, ct_find(name)); + if (c) { + lua_pushnumber(L, curse_geteffect(c)); + return 1; + } + } + return 0; } static int tolua_unit_has_attrib(lua_State *L) { unit *self = (unit *)tolua_tousertype(L, 1, 0); const char *name = tolua_tostring(L, 2, 0); - attrib * a = a_find(self->attribs, at_find(name)); + attrib * a = self->attribs; + while (a) { + if (strcmp(a->type->name, name) == 0) { + break; + } + a = a->nexttype; + } lua_pushboolean(L, a != NULL); return 1; } @@ -972,7 +986,7 @@ void tolua_unit_open(lua_State * L) tolua_function(L, TOLUA_CAST "clear_orders", &tolua_unit_clear_orders); tolua_variable(L, TOLUA_CAST "orders", &tolua_unit_get_orders, 0); - tolua_function(L, TOLUA_CAST "is_cursed", &tolua_unit_is_cursed); + tolua_function(L, TOLUA_CAST "get_curse", &tolua_unit_get_curse); tolua_function(L, TOLUA_CAST "has_attrib", &tolua_unit_has_attrib); /* key-attributes for named flags: */ @@ -1026,6 +1040,7 @@ void tolua_unit_open(lua_State * L) tolua_variable(L, TOLUA_CAST "race", &tolua_unit_get_race, tolua_unit_set_race); tolua_variable(L, TOLUA_CAST "hp_max", &tolua_unit_get_hpmax, 0); + tolua_variable(L, TOLUA_CAST "aura_max", &tolua_unit_get_auramax, 0); tolua_function(L, TOLUA_CAST "show", &tolua_bufunit); } diff --git a/src/bindings.c b/src/bindings.c index 1d6270b35..33d609ee0 100755 --- a/src/bindings.c +++ b/src/bindings.c @@ -92,6 +92,20 @@ TOLUA_PKG(locale); TOLUA_PKG(log); TOLUA_PKG(game); +int tolua_toid(lua_State * L, int idx, int def) +{ + int no = 0; + int type = lua_type(L, idx); + if (type == LUA_TNUMBER) { + no = (int)tolua_tonumber(L, idx, def); + } + else { + const char *str = tolua_tostring(L, idx, NULL); + no = str ? atoi36(str) : def; + } + return no; +} + int log_lua_error(lua_State * L) { const char *error = lua_tostring(L, -1); @@ -217,7 +231,7 @@ static int tolua_setkey(lua_State * L) return 0; } -static int tolua_rng_int(lua_State * L) +static int tolua_random(lua_State * L) { lua_pushinteger(L, rng_int()); return 1; @@ -907,7 +921,7 @@ static int init_data(const char *filename, const char *catalog) if (l) { return l; } - if (turn < 0) { + if (turn <= 0) { turn = first_turn(); } return 0; @@ -976,6 +990,12 @@ static void parse_inifile(lua_State * L, const dictionary * d, const char *secti } } +static int lua_rng_default(lua_State *L) { + UNUSED_ARG(L); + random_source_inject_constant(0); + return 0; +} + void tolua_bind_open(lua_State * L); int tolua_bindings_open(lua_State * L, const dictionary *inifile) @@ -997,6 +1017,14 @@ int tolua_bindings_open(lua_State * L, const dictionary *inifile) tolua_module(L, NULL, 0); tolua_beginmodule(L, NULL); { + tolua_module(L, TOLUA_CAST "rng", 1); + tolua_beginmodule(L, TOLUA_CAST "rng"); + { + tolua_function(L, TOLUA_CAST "inject", lua_rng_default); + tolua_function(L, TOLUA_CAST "random", tolua_random); + } + tolua_endmodule(L); + tolua_function(L, TOLUA_CAST "rng_int", tolua_random); tolua_cclass(L, TOLUA_CAST "alliance", TOLUA_CAST "alliance", TOLUA_CAST "", NULL); tolua_beginmodule(L, TOLUA_CAST "alliance"); @@ -1086,7 +1114,6 @@ int tolua_bindings_open(lua_State * L, const dictionary *inifile) tolua_function(L, TOLUA_CAST "get_key", tolua_getkey); tolua_function(L, TOLUA_CAST "set_key", tolua_setkey); tolua_function(L, TOLUA_CAST "translate", &tolua_translate); - tolua_function(L, TOLUA_CAST "rng_int", tolua_rng_int); tolua_function(L, TOLUA_CAST "spells", tolua_get_spells); tolua_function(L, TOLUA_CAST "read_xml", tolua_read_xml); } tolua_endmodule(L); diff --git a/src/bindings.h b/src/bindings.h index 354bb199a..5cb64a038 100755 --- a/src/bindings.h +++ b/src/bindings.h @@ -18,6 +18,7 @@ extern "C" { struct _dictionary_; struct selist; + int tolua_toid(struct lua_State *L, int idx, int def); int tolua_sqlite_open(struct lua_State *L); int tolua_bindings_open(struct lua_State *L, const struct _dictionary_ *d); int tolua_itemlist_next(struct lua_State *L); diff --git a/src/calendar.c b/src/calendar.c index d2a70c974..6fd717429 100644 --- a/src/calendar.c +++ b/src/calendar.c @@ -1,20 +1,35 @@ #include #include "calendar.h" +#include "move.h" /* storms */ + #include #include #include +#include int first_month = 0; -int weeks_per_month = 4; +int weeks_per_month = 3; int months_per_year = 12; -char **seasonnames = NULL; +const char *seasonnames[CALENDAR_SEASONS] = { "winter", "spring", "summer", "fall" }; char **weeknames = NULL; char **weeknames2 = NULL; -char **monthnames = NULL; int *month_season = NULL; -char *agename = NULL; -int seasons = 0; + +const char *calendar_month(int index) +{ + static char result[20]; + snprintf(result, sizeof(result), "month_%d", index + 1); + return result; +} + +const char *calendar_era(void) +{ + static char result[20]; + int era = config_get_int("game.era", 1); + snprintf(result, sizeof(result), "age_%d", era); + return result; +} int first_turn(void) { @@ -27,8 +42,7 @@ const gamedate *get_gamedate(int turn, gamedate * gd) int t = turn - first_turn(); assert(gd); - if (t < 0) - t = turn; + assert(t>=0); gd->week = t % weeks_per_month; /* 0 - weeks_per_month-1 */ gd->month = (t / weeks_per_month + first_month) % months_per_year; /* 0 - months_per_year-1 */ @@ -41,36 +55,23 @@ void calendar_cleanup(void) { int i; - free(agename); - - if (seasonnames) { - for (i = 0; i != seasons; ++i) { - free(seasonnames[i]); - } - free(seasonnames); - seasonnames = 0; - } - - if (monthnames) { - for (i = 0; i != months_per_year; ++i) { - free(monthnames[i]); - } - free(storms); - storms = 0; - free(month_season); - month_season = 0; - free(monthnames); - monthnames = 0; - } - for (i = 0; i != weeks_per_month; ++i) { if (weeknames) free(weeknames[i]); if (weeknames2) free(weeknames2[i]); } + + free(storms); + storms = 0; + free(month_season); + month_season = 0; free(weeknames); weeknames = 0; free(weeknames2); weeknames2 = 0; + + first_month = 0; + weeks_per_month = 3; + months_per_year = 12; } diff --git a/src/calendar.h b/src/calendar.h index d398cd77c..f8efa392c 100644 --- a/src/calendar.h +++ b/src/calendar.h @@ -11,17 +11,12 @@ extern "C" { SEASON_SUMMER, SEASON_AUTUMN }; - - extern char *agename; - extern int first_month; - - extern int seasons; - extern char **seasonnames; +#define CALENDAR_SEASONS 4 + extern const char *seasonnames[CALENDAR_SEASONS]; extern int months_per_year; - extern char **monthnames; extern int *month_season; - extern int *storms; /* in movement.c */ + extern int first_month; extern char **weeknames; extern char **weeknames2; @@ -36,6 +31,8 @@ extern "C" { const gamedate *get_gamedate(int turn, gamedate * gd); void calendar_cleanup(void); +const char *calendar_month(int index); +const char *calendar_era(void); int first_turn(void); #ifdef __cplusplus diff --git a/src/calendar.test.c b/src/calendar.test.c new file mode 100644 index 000000000..e858747ec --- /dev/null +++ b/src/calendar.test.c @@ -0,0 +1,98 @@ +#include + +#include "calendar.h" + +#include + +#include +#include "tests.h" + +static void test_calendar_config(CuTest * tc) +{ + gamedate gd; + + test_setup(); + get_gamedate(0, &gd); + CuAssertIntEquals(tc, 0, first_turn()); + config_set_int("game.start", 42); + CuAssertIntEquals(tc, 42, first_turn()); + test_cleanup(); +} + +static void test_calendar(CuTest * tc) +{ + gamedate gd; + + test_setup(); + get_gamedate(0, &gd); + CuAssertIntEquals(tc, 1, gd.year); + CuAssertIntEquals(tc, 0, gd.season); + CuAssertIntEquals(tc, 0, gd.month); + CuAssertIntEquals(tc, 0, gd.week); + + get_gamedate(1, &gd); + CuAssertIntEquals(tc, 1, gd.year); + CuAssertIntEquals(tc, 0, gd.season); + CuAssertIntEquals(tc, 0, gd.month); + CuAssertIntEquals(tc, 1, gd.week); + + get_gamedate(weeks_per_month, &gd); + CuAssertIntEquals(tc, 1, gd.year); + CuAssertIntEquals(tc, 0, gd.season); + CuAssertIntEquals(tc, 1, gd.month); + CuAssertIntEquals(tc, 0, gd.week); + + get_gamedate(weeks_per_month*months_per_year, &gd); + CuAssertIntEquals(tc, 2, gd.year); + CuAssertIntEquals(tc, 0, gd.season); + CuAssertIntEquals(tc, 0, gd.month); + CuAssertIntEquals(tc, 0, gd.week); + + config_set_int("game.start", 42); + get_gamedate(42, &gd); + CuAssertIntEquals(tc, 1, gd.year); + CuAssertIntEquals(tc, 0, gd.season); + CuAssertIntEquals(tc, 0, gd.month); + CuAssertIntEquals(tc, 0, gd.week); + + first_month = 2; + get_gamedate(42, &gd); + CuAssertIntEquals(tc, 1, gd.year); + CuAssertIntEquals(tc, 0, gd.season); + CuAssertIntEquals(tc, 2, gd.month); + CuAssertIntEquals(tc, 0, gd.week); + + test_cleanup(); +} + +static void test_calendar_season(CuTest * tc) +{ + gamedate gd; + + test_setup(); + month_season = calloc(months_per_year, sizeof(int)); + + get_gamedate(0, &gd); + CuAssertIntEquals(tc, 1, gd.year); + CuAssertIntEquals(tc, 0, gd.season); + CuAssertIntEquals(tc, 0, gd.month); + CuAssertIntEquals(tc, 0, gd.week); + + month_season[1] = 1; + get_gamedate(weeks_per_month + 1, &gd); + CuAssertIntEquals(tc, 1, gd.year); + CuAssertIntEquals(tc, 1, gd.season); + CuAssertIntEquals(tc, 1, gd.month); + CuAssertIntEquals(tc, 1, gd.week); + + test_cleanup(); +} + +CuSuite *get_calendar_suite(void) +{ + CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_calendar_config); + SUITE_ADD_TEST(suite, test_calendar); + SUITE_ADD_TEST(suite, test_calendar_season); + return suite; +} diff --git a/src/convert.c b/src/convert.c new file mode 100644 index 000000000..a67c5a287 --- /dev/null +++ b/src/convert.c @@ -0,0 +1,44 @@ +#include + + +#ifdef USE_LIBXML2 +#include +#include +#endif +#include +#include +#include + +#include + +#include + +static int usage(void) { + return -1; +} + +int main(int argc, char **argv) { + const char *mode; + + register_races(); +#ifdef USE_LIBXML2 + register_xmlreader(); +#endif + if (argc < 2) return usage(); + mode = argv[1]; +#ifdef USE_LIBXML2 + if (strcmp(mode, "rules")==0) { + const char *xmlfile, *catalog; + if (argc < 4) return usage(); + xmlfile = argv[2]; + catalog = argv[3]; + read_xml(xmlfile, catalog); + write_rules("rules.dat"); + return 0; + } +#endif + if (strcmp(mode, "po")==0) { + return 0; + } + return usage(); +} diff --git a/src/creport.c b/src/creport.c index dc897e055..10dc4c323 100644 --- a/src/creport.c +++ b/src/creport.c @@ -87,19 +87,23 @@ bool opt_cr_absolute_coords = false; /* globals */ #define C_REPORT_VERSION 66 -#define TAG_LOCALE "de" -#ifdef TAG_LOCALE +struct locale *crtag_locale(void) { + static struct locale * lang; + static int config; + if (config_changed(&config)) { + const char *lname = config_get("creport.tags"); + lang = get_locale(lname ? lname : "de"); + } + return lang; +} + static const char *crtag(const char *key) { /* TODO: those locale lookups are shit, but static kills testing */ const char *result; - const struct locale *lang = get_locale(TAG_LOCALE); - result = LOC(lang, key); + result = LOC(crtag_locale(), key); return result; } -#else -#define crtag(x) (x) -#endif /* * translation table */ @@ -134,7 +138,7 @@ static const char *translate(const char *key, const char *value) return crtag(key); } -static void write_translations(FILE * F) +static void report_translations(FILE * F) { int i; fputs("TRANSLATION\n", F); @@ -276,7 +280,7 @@ cr_output_curses(struct stream *out, const faction * viewer, const void *obj, ob stream_printf(out, "EFFECTS\n"); } stream_printf(out, "\"%d %s\"\n", data->value, translate(key, - LOC(default_locale, key))); + LOC(viewer->locale, key))); } a = a->next; } @@ -1148,7 +1152,7 @@ cr_borders(const region * r, const faction * f, seen_mode mode, FILE * F) const char *bname = border_name(b, r, f, GF_PURE); bname = mkname("border", bname); fprintf(F, "GRENZE %d\n", ++g); - fprintf(F, "\"%s\";typ\n", LOC(default_locale, bname)); + fprintf(F, "\"%s\";typ\n", LOC(f->locale, bname)); fprintf(F, "%d;richtung\n", d); if (!b->type->transparent(b, f)) fputs("1;opaque\n", F); @@ -1412,9 +1416,6 @@ static void cr_output_region(FILE * F, report_context * ctx, region * r) } } } - if (r->land) { - print_items(F, r->land->items, f->locale); - } cr_output_curses_compat(F, f, r, TYP_REGION); cr_borders(r, f, r->seen.mode, F); if (r->seen.mode == seen_unit && is_astral(r) @@ -1497,7 +1498,7 @@ report_computer(const char *filename, report_context * ctx, const char *bom) static int rc_cache; if (era < 0) { - era = config_get_int("world.era", 1); + era = config_get_int("game.era", 1); } if (F == NULL) { perror(filename); @@ -1558,7 +1559,7 @@ report_computer(const char *filename, report_context * ctx, const char *bom) translate(prefix, LOC(f->locale, prefix))); } fprintf(F, "%d;Rekrutierungskosten\n", f->race->recruitcost); - fprintf(F, "%d;Anzahl Personen\n", count_all(f)); + fprintf(F, "%d;Anzahl Personen\n", f->num_people); fprintf(F, "\"%s\";Magiegebiet\n", magic_school[f->magiegebiet]); if (rc_changed(&rc_cache)) { @@ -1680,7 +1681,9 @@ report_computer(const char *filename, report_context * ctx, const char *bom) } } report_crtypes(F, f->locale); - write_translations(F); + if (f->locale!=crtag_locale()) { + report_translations(F); + } reset_translations(); fclose(F); return 0; diff --git a/src/economy.c b/src/economy.c index c210ee643..0bc056b56 100644 --- a/src/economy.c +++ b/src/economy.c @@ -468,7 +468,6 @@ static void recruit(unit * u, struct order *ord, request ** recruitorders) assert(rc); u_setrace(u, rc); -#if GUARD_DISABLES_RECRUIT /* 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(). */ @@ -476,13 +475,11 @@ static void recruit(unit * u, struct order *ord, request ** recruitorders) cmistake(u, ord, 70, MSG_EVENT); return; } -#endif if (rc == get_race(RC_INSECT)) { gamedate date; get_gamedate(turn, &date); if (date.season == 0 && r->terrain != newterrain(T_DESERT)) { -#ifdef INSECT_POTION bool usepotion = false; unit *u2; @@ -492,7 +489,6 @@ static void recruit(unit * u, struct order *ord, request ** recruitorders) break; } if (!usepotion) -#endif { cmistake(u, ord, 98, MSG_EVENT); return; @@ -579,7 +575,7 @@ void give_control(unit * u, unit * u2) assert(u->building == u2->building); if (f == u->faction) { - building *b = largestbuilding(r, &cmp_current_owner, false); + building *b = largestbuilding(r, cmp_current_owner, false); if (b == u->building) { friendly_takeover(r, u2->faction); } @@ -802,40 +798,99 @@ void economics(region * r) } -/* ------------------------------------------------------------- */ +static void mod_skill(const resource_mod *mod, skill_t sk, int *skill) { + skill_t msk; + assert(mod->type == RMT_PROD_SKILL); + msk = (skill_t)mod->value.sa[0]; + if (msk == NOSKILL || msk == sk) { + *skill += mod->value.sa[1]; + } +} + +static struct message * get_modifiers(unit *u, skill_t sk, const resource_type *rtype, variant *savep, int *skillp) { + struct building *b = inside_building(u); + const struct building_type *btype = building_is_active(b) ? b->type : NULL; + int save_n = 1, save_d = 1; + int skill = 0; + int need_race = 0, need_bldg = 0; + resource_mod *mod; + const struct building_type *btype_needed = NULL; + + if (btype && btype->modifiers) { + for (mod = btype->modifiers; mod && mod->type != RMT_END; ++mod) { + if (mod->type == RMT_PROD_SKILL) { + mod_skill(mod, sk, &skill); + } + } + } + + for (mod = rtype->modifiers; mod && mod->type != RMT_END; ++mod) { + if (mod->btype == NULL || mod->btype == btype) { + if (mod->race == NULL || mod->race == u_race(u)) { + switch (mod->type) { + case RMT_PROD_SAVE: + if (savep) { + save_n *= mod->value.sa[0]; + save_d *= mod->value.sa[1]; + } + break; + case RMT_PROD_SKILL: + mod_skill(mod, sk, &skill); + break; + case RMT_PROD_REQUIRE: + if (mod->race) need_race |= 1; + if (mod->btype) { + need_bldg |= 1; + } + break; + default: + /* is not a production modifier, ignore it */ + break; + } + } + } + if (mod->type == RMT_PROD_REQUIRE) { + if (mod->race) need_race |= 2; + if (mod->btype) { + btype_needed = mod->btype; + need_bldg |= 2; + } + } + } + if (need_race == 2) { + return msg_error(u, u->thisorder, 117); + } + if (btype_needed && need_bldg == 2) { + return msg_feedback(u, u->thisorder, "building_needed", "building", btype_needed->_name); + } + *skillp = skill; + if (savep) *savep = frac_make(save_n, save_d); + return NULL; +} static void manufacture(unit * u, const item_type * itype, int want) { int n; - int skill; int minskill = itype->construction->minskill; skill_t sk = itype->construction->skill; + message *msg; + int skill_mod; - skill = effskill(u, sk, 0); - skill = - skillmod(itype->rtype->attribs, u, u->region, sk, skill, SMF_PRODUCTION); - - if (skill < 0) { - /* an error occured */ - int err = -skill; - cmistake(u, u->thisorder, err, MSG_PRODUCE); + msg = get_modifiers(u, sk, itype->rtype, NULL, &skill_mod); + if (msg) { + ADDMSG(&u->faction->msgs, msg); return; } if (want == 0) { want = maxbuild(u, itype->construction); } - n = build(u, itype->construction, 0, want); + n = build(u, itype->construction, 0, want, skill_mod); switch (n) { case ENEEDSKILL: ADDMSG(&u->faction->msgs, msg_feedback(u, u->thisorder, "skill_needed", "skill", sk)); return; - case EBUILDINGREQ: - ADDMSG(&u->faction->msgs, - msg_feedback(u, u->thisorder, "building_needed", "building", - itype->construction->btype->_name)); - return; case ELOWSKILL: ADDMSG(&u->faction->msgs, msg_feedback(u, u->thisorder, "manufacture_skills", @@ -883,41 +938,6 @@ enum { AFL_LOWSKILL = 1 << 1 }; -struct message * get_modifiers(unit *u, const resource_mod *mod, variant *savep, int *skillp) { - struct building *b = inside_building(u); - const struct building_type *btype = building_is_active(b) ? b->type : NULL; - int save_n = 1, save_d = 1; - int skill = 0; - - for (; mod->flags != 0; ++mod) { - if (mod->btype == NULL || mod->btype == btype) { - if (mod->race == NULL || mod->race == u_race(u)) { - if (mod->flags & RMF_SAVEMATERIAL) { - save_n *= mod->value.sa[0]; - save_d *= mod->value.sa[1]; - } - if (mod->flags & RMF_SKILL) { - skill += mod->value.i; - } - } - } else if (mod->flags & RMF_REQUIREDBUILDING) { - return msg_error(u, u->thisorder, 104); - } - } - *skillp = skill; - assert(save_n < SHRT_MAX); - assert(save_n > SHRT_MIN); - assert(save_d < SHRT_MAX); - assert(save_d > SHRT_MIN); - savep->sa[0] = (short)save_n; - savep->sa[1] = (short)save_d; - return NULL; -} - -static resource_limit *get_resourcelimit(const resource_type *rtype) { - return rtype->limit; -} - static void allocate_resource(unit * u, const resource_type * rtype, int want) { const item_type *itype = resource2item(rtype); @@ -925,19 +945,19 @@ static void allocate_resource(unit * u, const resource_type * rtype, int want) int dm = 0; allocation_list *alist; allocation *al; - resource_limit *rdata = get_resourcelimit(rtype); const resource_type *rring; int amount, skill, skill_mod = 0; variant save_mod; + skill_t sk; /* momentan kann man keine ressourcen abbauen, wenn man daf�r * Materialverbrauch hat: */ assert(itype != NULL && (itype->construction == NULL || itype->construction->materials == NULL)); - assert(rdata != NULL); - if (rdata->limit != NULL) { - int avail = rdata->limit(r, rtype); + sk = itype->construction->skill; + if (!rtype->raw) { + int avail = limit_resource(r, rtype); if (avail <= 0) { cmistake(u, u->thisorder, 121, MSG_PRODUCE); return; @@ -949,8 +969,8 @@ static void allocate_resource(unit * u, const resource_type * rtype, int want) return; } - if (rdata->modifiers) { - message *msg = get_modifiers(u, rdata->modifiers, &save_mod, &skill_mod); + if (rtype->modifiers) { + message *msg = get_modifiers(u, sk, rtype, &save_mod, &skill_mod); if (msg) { ADDMSG(&u->faction->msgs, msg); return; @@ -978,17 +998,14 @@ static void allocate_resource(unit * u, const resource_type * rtype, int want) } } - assert(itype->construction->skill != 0 - || "limited resource needs a required skill for making it"); - skill = effskill(u, itype->construction->skill, 0); + assert(sk != NOSKILL || "limited resource needs a required skill for making it"); + skill = effskill(u, sk, 0); if (skill == 0) { - skill_t sk = itype->construction->skill; add_message(&u->faction->msgs, msg_feedback(u, u->thisorder, "skill_needed", "skill", sk)); return; } if (skill < itype->construction->minskill) { - skill_t sk = itype->construction->skill; add_message(&u->faction->msgs, msg_feedback(u, u->thisorder, "manufacture_skills", "skill minskill product", sk, itype->construction->minskill, @@ -1126,17 +1143,17 @@ attrib_allocation(const resource_type * rtype, region * r, allocation * alist) { allocation *al; int nreq = 0; - resource_limit *rdata = get_resourcelimit(rtype); int avail = INT_MAX; for (al = alist; al; al = al->next) { nreq += required(al->want, al->save); } - if (rdata->limit) { - avail = rdata->limit(r, rtype); - if (avail < 0) + if (!rtype->raw) { + avail = limit_resource(r, rtype); + if (avail < 0) { avail = 0; + } } avail = MIN(avail, nreq); @@ -1151,10 +1168,11 @@ attrib_allocation(const resource_type * rtype, region * r, allocation * alist) nreq -= want; al->get = x * al->save.sa[1] / al->save.sa[0]; al->get = MIN(al->want, al->get); - if (rdata->produce) { + if (!rtype->raw) { int use = required(al->get, al->save); - if (use) - rdata->produce(r, rtype, use); + if (use) { + produce_resource(r, rtype, use); + } } } } @@ -1166,21 +1184,15 @@ typedef void(*allocate_function) (const resource_type *, struct region *, static allocate_function get_allocator(const struct resource_type *rtype) { - resource_limit *rdata = get_resourcelimit(rtype); - - if (rdata) { - if (rdata->limit != NULL) { - return attrib_allocation; - } + if (rtype->raw) { return leveled_allocation; } - return NULL; + return attrib_allocation; } void split_allocations(region * r) { allocation_list **p_alist = &allocations; - freset(r, RF_SELECT); while (*p_alist) { allocation_list *alist = *p_alist; const resource_type *rtype = alist->type; @@ -1188,7 +1200,6 @@ void split_allocations(region * r) const item_type *itype = resource2item(rtype); allocation **p_al = &alist->data; - freset(r, RF_SELECT); alloc(rtype, r, alist->data); while (*p_al) { @@ -1197,7 +1208,6 @@ void split_allocations(region * r) assert(itype || !"not implemented for non-items"); i_change(&al->unit->items, itype, al->get); produceexp(al->unit, itype->construction->skill, al->unit->number); - fset(r, RF_SELECT); } if (al->want == INT_MAX) al->want = al->get; @@ -1220,18 +1230,13 @@ static void create_potion(unit * u, const potion_type * ptype, int want) if (want == 0) { want = maxbuild(u, ptype->itype->construction); } - built = build(u, ptype->itype->construction, 0, want); + built = build(u, ptype->itype->construction, 0, want, 0); switch (built) { case ELOWSKILL: case ENEEDSKILL: /* no skill, or not enough skill points to build */ cmistake(u, u->thisorder, 50, MSG_PRODUCE); break; - case EBUILDINGREQ: - ADDMSG(&u->faction->msgs, - msg_feedback(u, u->thisorder, "building_needed", "building", - ptype->itype->construction->btype->_name)); - break; case ECOMPLETE: assert(0); break; @@ -1255,12 +1260,10 @@ static void create_potion(unit * u, const potion_type * ptype, int want) void make_item(unit * u, const item_type * itype, int want) { if (itype->construction && fval(itype->rtype, RTF_LIMITED)) { -#if GUARD_DISABLES_PRODUCTION == 1 if (is_guarded(u->region, u)) { cmistake(u, u->thisorder, 70, MSG_EVENT); return; } -#endif allocate_resource(u, itype->rtype, want); } else { @@ -2033,20 +2036,6 @@ static void expandstealing(region * r, request * stealorders) if (u && u->region == r) { n = get_pooled(u, rsilver, GET_ALL, INT_MAX); } -#ifndef GOBLINKILL - if (oa[i].type.goblin) { /* Goblin-Spezialklau */ - int uct = 0; - unit *u2; - assert(effskill(oa[i].unit, SK_STEALTH) >= 4 - || !"this goblin\'s skill is too low"); - for (u2 = r->units; u2; u2 = u2->next) { - if (u2->faction == u->faction) { - uct += maintenance_cost(u2); - } - } - n -= uct * 2; - } -#endif if (n > 10 && rplane(r) && (rplane(r)->flags & PFL_NOALLIANCES)) { /* In Questen nur reduziertes Klauen */ n = 10; @@ -2665,8 +2654,7 @@ expandwork(region * r, request * work_begin, request * work_end, int maxwork) const curse_type *blessedharvest_ct = ct_find("blessedharvest"); if (blessedharvest_ct) { int happy = - (int)curse_geteffect(get_curse(r->attribs, blessedharvest_ct)); - happy = MIN(happy, jobs); + (int)(jobs * curse_geteffect(get_curse(r->attribs, blessedharvest_ct))); earnings += happy; } } @@ -2936,7 +2924,7 @@ static void peasant_taxes(region * r) unit *u; building *b; int money; - int maxsize; + int level; f = region_get_owner(r); if (f == NULL || is_mourning(r, turn)) { @@ -2954,10 +2942,10 @@ static void peasant_taxes(region * r) if (u == NULL || u->faction != f) return; - maxsize = buildingeffsize(b, false); - if (maxsize > 0) { - double taxfactor = money * b->type->taxes(b, maxsize); - double morale = MORALE_TAX_FACTOR * money * region_get_morale(r); + level = buildingeffsize(b, false); + if (level > 0) { + double taxfactor = (double)money * level / building_taxes(b); + double morale = (double)money * region_get_morale(r) / MORALE_TAX_FACTOR; if (taxfactor > morale) { taxfactor = morale; } diff --git a/src/economy.test.c b/src/economy.test.c index b77c69a86..802610880 100644 --- a/src/economy.test.c +++ b/src/economy.test.c @@ -316,8 +316,12 @@ static void test_recruit(CuTest *tc) { f = test_create_faction(0); u = test_create_unit(f, test_create_region(0, 0, 0)); CuAssertIntEquals(tc, 1, u->number); + CuAssertIntEquals(tc, 1, f->num_people); + CuAssertIntEquals(tc, 1, f->num_units); add_recruits(u, 1, 1); CuAssertIntEquals(tc, 2, u->number); + CuAssertIntEquals(tc, 2, f->num_people); + CuAssertIntEquals(tc, 1, f->num_units); CuAssertPtrEquals(tc, u, f->units); CuAssertPtrEquals(tc, NULL, u->nextF); CuAssertPtrEquals(tc, NULL, u->prevF); @@ -341,12 +345,118 @@ static void test_income(CuTest *tc) test_cleanup(); } -static void test_make_item(CuTest *tc) { +static void test_modify_material(CuTest *tc) { + unit *u; + struct item_type *itype; + resource_type *rtype; + resource_mod *mod; + + test_setup(); + init_resources(); + + u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); + set_level(u, SK_WEAPONSMITH, 1); + + /* the unit's race gets 2x savings on iron used to produce goods */ + itype = test_create_itemtype("iron"); + rtype = itype->rtype; + mod = rtype->modifiers = calloc(2, sizeof(resource_mod)); + mod[0].type = RMT_USE_SAVE; + mod[0].value = frac_make(2, 1); + mod[0].race = u_race(u); + + itype = test_create_itemtype("sword"); + make_item(u, itype, 1); + CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error_cannotmake")); + CuAssertIntEquals(tc, 0, get_item(u, itype)); + test_clear_messages(u->faction); + itype->construction = calloc(1, sizeof(construction)); + itype->construction->skill = SK_WEAPONSMITH; + itype->construction->minskill = 1; + itype->construction->maxsize = 1; + itype->construction->reqsize = 1; + itype->construction->materials = calloc(2, sizeof(requirement)); + itype->construction->materials[0].rtype = rtype; + itype->construction->materials[0].number = 2; + + set_item(u, rtype->itype, 1); /* 1 iron should get us 1 sword */ + make_item(u, itype, 1); + CuAssertIntEquals(tc, 1, get_item(u, itype)); + CuAssertIntEquals(tc, 0, get_item(u, rtype->itype)); + + u_setrace(u, test_create_race("smurf")); + set_item(u, rtype->itype, 2); /* 2 iron should be required now */ + make_item(u, itype, 1); + CuAssertIntEquals(tc, 2, get_item(u, itype)); + CuAssertIntEquals(tc, 0, get_item(u, rtype->itype)); + + test_cleanup(); +} + +static void test_modify_skill(CuTest *tc) { + unit *u; + struct item_type *itype; + /* building_type *btype; */ + resource_type *rtype; + resource_mod *mod; + + test_setup(); + init_resources(); + + u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); + set_level(u, SK_WEAPONSMITH, 1); + + itype = test_create_itemtype("iron"); + rtype = itype->rtype; + + itype = test_create_itemtype("sword"); + make_item(u, itype, 1); + CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error_cannotmake")); + CuAssertIntEquals(tc, 0, get_item(u, itype)); + test_clear_messages(u->faction); + itype->construction = calloc(1, sizeof(construction)); + itype->construction->skill = SK_WEAPONSMITH; + itype->construction->minskill = 1; + itype->construction->maxsize = -1; + itype->construction->reqsize = 1; + itype->construction->materials = calloc(2, sizeof(requirement)); + itype->construction->materials[0].rtype = rtype; + itype->construction->materials[0].number = 1; + + /* our race gets a +1 bonus to the item's production skill */ + mod = itype->rtype->modifiers = calloc(2, sizeof(resource_mod)); + mod[0].type = RMT_PROD_SKILL; + mod[0].value.sa[0] = SK_WEAPONSMITH; + mod[0].value.sa[1] = 1; + mod[0].race = u_race(u); + + set_item(u, rtype->itype, 2); /* 2 iron should get us 2 swords */ + make_item(u, itype, 2); + CuAssertIntEquals(tc, 2, get_item(u, itype)); + CuAssertIntEquals(tc, 0, get_item(u, rtype->itype)); + + mod[0].value.sa[0] = NOSKILL; /* match any skill */ + set_item(u, rtype->itype, 2); + make_item(u, itype, 2); + CuAssertIntEquals(tc, 4, get_item(u, itype)); + CuAssertIntEquals(tc, 0, get_item(u, rtype->itype)); + + + u_setrace(u, test_create_race("smurf")); + set_item(u, rtype->itype, 2); + make_item(u, itype, 1); /* only enough skill to make 1 now */ + CuAssertIntEquals(tc, 5, get_item(u, itype)); + CuAssertIntEquals(tc, 1, get_item(u, rtype->itype)); + + test_cleanup(); +} + + +static void test_modify_production(CuTest *tc) { unit *u; struct item_type *itype; const struct resource_type *rt_silver; resource_type *rtype; - resource_limit *rdata; double d = 0.6; test_setup(); @@ -380,8 +490,7 @@ static void test_make_item(CuTest *tc) { itype->construction->materials = 0; rtype->flags |= RTF_LIMITED; rmt_create(rtype); - rdata = rtype->limit = calloc(1, sizeof(resource_limit)); - add_resource(u->region, 1, 300, 150, rtype); + add_resource(u->region, 1, 300, 150, rtype); /* there are 300 stones at level 1 */ CuAssertIntEquals(tc, 300, region_getresource(u->region, rtype)); set_level(u, SK_ALCHEMY, 10); @@ -390,11 +499,11 @@ static void test_make_item(CuTest *tc) { CuAssertIntEquals(tc, 11, get_item(u, itype)); CuAssertIntEquals(tc, 290, region_getresource(u->region, rtype)); /* used 10 stones to make 10 stones */ - rdata->modifiers = calloc(2, sizeof(resource_mod)); - rdata->modifiers[0].flags = RMF_SAVEMATERIAL; - rdata->modifiers[0].race = u->_race; - rdata->modifiers[0].value.sa[0] = (short)(0.5+100*d); - rdata->modifiers[0].value.sa[1] = 100; + rtype->modifiers = calloc(2, sizeof(resource_mod)); + rtype->modifiers[0].type = RMT_PROD_SAVE; + rtype->modifiers[0].race = u->_race; + rtype->modifiers[0].value.sa[0] = (short)(0.5+100*d); + rtype->modifiers[0].value.sa[1] = 100; make_item(u, itype, 10); split_allocations(u->region); CuAssertIntEquals(tc, 21, get_item(u, itype)); @@ -405,17 +514,39 @@ static void test_make_item(CuTest *tc) { CuAssertIntEquals(tc, 22, get_item(u, itype)); CuAssertIntEquals(tc, 283, region_getresource(u->region, rtype)); /* no free lunches */ - rdata->modifiers[0].value = frac_make(1, 2); + rtype->modifiers[0].value = frac_make(1, 2); make_item(u, itype, 6); split_allocations(u->region); CuAssertIntEquals(tc, 28, get_item(u, itype)); CuAssertIntEquals(tc, 280, region_getresource(u->region, rtype)); /* 50% saving = 3 stones make 6 stones */ - rdata->modifiers[0].flags = RMF_REQUIREDBUILDING; - rdata->modifiers[0].race = NULL; - rdata->modifiers[0].btype = bt_get_or_create("mine"); + rtype->modifiers[0].type = RMT_PROD_REQUIRE; + rtype->modifiers[0].race = NULL; + rtype->modifiers[0].btype = bt_get_or_create("mine"); + + test_clear_messages(u->faction); make_item(u, itype, 10); - CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error104")); + CuAssertIntEquals(tc, 28, get_item(u, itype)); + CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "building_needed")); + + rtype->modifiers[0].type = RMT_PROD_REQUIRE; + rtype->modifiers[0].race = test_create_race("smurf"); + rtype->modifiers[0].btype = NULL; + + test_clear_messages(u->faction); + make_item(u, itype, 10); + CuAssertIntEquals(tc, 28, get_item(u, itype)); + CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error117")); + + rtype->modifiers[1].type = RMT_PROD_REQUIRE; + rtype->modifiers[1].race = u_race(u); + rtype->modifiers[1].btype = NULL; + + test_clear_messages(u->faction); + make_item(u, itype, 10); + CuAssertPtrEquals(tc, NULL, u->faction->msgs); + split_allocations(u->region); + CuAssertIntEquals(tc, 38, get_item(u, itype)); test_cleanup(); } @@ -426,7 +557,9 @@ CuSuite *get_economy_suite(void) SUITE_ADD_TEST(suite, test_give_control_building); SUITE_ADD_TEST(suite, test_give_control_ship); SUITE_ADD_TEST(suite, test_income); - SUITE_ADD_TEST(suite, test_make_item); + SUITE_ADD_TEST(suite, test_modify_production); + SUITE_ADD_TEST(suite, test_modify_skill); + SUITE_ADD_TEST(suite, test_modify_material); SUITE_ADD_TEST(suite, test_steal_okay); SUITE_ADD_TEST(suite, test_steal_ocean); SUITE_ADD_TEST(suite, test_steal_nosteal); diff --git a/src/eressea.c b/src/eressea.c index 8001355f1..a9dddd130 100755 --- a/src/eressea.c +++ b/src/eressea.c @@ -8,9 +8,6 @@ #if MUSEUM_MODULE #include #endif -#if ARENA_MODULE -#include -#endif #include #include #include @@ -73,13 +70,9 @@ void game_init(void) register_spells(); register_names(); register_resources(); - register_buildings(); register_itemfunctions(); #if MUSEUM_MODULE register_museum(); -#endif -#if ARENA_MODULE - register_arena(); #endif wormholes_register(); diff --git a/src/give.c b/src/give.c index b52168647..060ecbbb9 100644 --- a/src/give.c +++ b/src/give.c @@ -19,6 +19,7 @@ /* kernel includes */ #include +#include #include #include #include @@ -169,17 +170,35 @@ int give_quota(const unit * src, const unit * dst, const item_type * type, return n; } +static void +give_horses(unit * s, const item_type * itype, int n) +{ + region *r = s->region; + if (r->land) { + rsethorses(r, rhorses(r) + n); + } +} + +static void +give_money(unit * s, const item_type * itype, int n) +{ + region *r = s->region; + if (r->land) { + rsetmoney(r, rmoney(r) + n); + } +} + int give_item(int want, const item_type * itype, unit * src, unit * dest, struct order *ord) { short error = 0; - int n, r; + int n, delta; assert(itype != NULL); n = get_pooled(src, item2resource(itype), GET_SLACK | GET_POOLED_SLACK, want); n = MIN(want, n); - r = n; + delta = n; if (dest && src->faction != dest->faction && src->faction->age < GiveRestriction()) { if (ord != NULL) { @@ -200,18 +219,19 @@ struct order *ord) else if (itype->flags & ITF_CURSED) { error = 25; } - else if (itype->give == NULL || itype->give(src, dest, itype, n, ord) != 0) { + else { int use = use_pooled(src, item2resource(itype), GET_SLACK, n); + if (use < n) use += use_pooled(src, item2resource(itype), GET_POOLED_SLACK, n - use); if (dest) { - r = give_quota(src, dest, itype, n); - i_change(&dest->items, itype, r); + delta = give_quota(src, dest, itype, n); + i_change(&dest->items, itype, delta); #ifdef RESERVE_GIVE #ifdef RESERVE_DONATIONS - change_reservation(dest, itype, r); + change_reservation(dest, itype, delta); #else if (src->faction == dest->faction) { change_reservation(dest, item2resource(itype), r); @@ -221,14 +241,23 @@ struct order *ord) #if MUSEUM_MODULE && defined(TODO) /* TODO: use a trigger for the museum warden! */ if (a_find(dest->attribs, &at_warden)) { - warden_add_give(src, dest, itype, r); + warden_add_give(src, dest, itype, delta); } #endif - handle_event(dest->attribs, "receive", src); } - handle_event(src->attribs, "give", dest); + else { + /* return horses to the region */ + if (itype->construction && itype->flags & ITF_ANIMAL) { + if (itype->construction->skill == SK_HORSE_TRAINING) { + give_horses(src, itype, n); + } + } + else if (itype->rtype == get_resourcetype(R_SILVER)) { + give_money(src, itype, n); + } + } } - add_give(src, dest, n, r, item2resource(itype), ord, error); + add_give(src, dest, n, delta, item2resource(itype), ord, error); if (error) return -1; return 0; @@ -502,6 +531,23 @@ void give_unit(unit * u, unit * u2, order * ord) } } return; + } else { + int err = checkunitnumber(u2->faction, 1); + if (err) { + if (err == 1) { + ADDMSG(&u->faction->msgs, + msg_feedback(u, ord, + "too_many_units_in_alliance", + "allowed", rule_alliance_limit())); + } + else { + ADDMSG(&u->faction->msgs, + msg_feedback(u, ord, + "too_many_units_in_faction", + "allowed", rule_faction_limit())); + } + return; + } } if (!alliedunit(u2, u->faction, HELP_GIVE) && ucontact(u2, u) == 0) { diff --git a/src/give.test.c b/src/give.test.c index 1767dec36..33a9ebfd9 100644 --- a/src/give.test.c +++ b/src/give.test.c @@ -54,26 +54,12 @@ static void setup_give(struct give *env) { } } -static void test_give_unit_to_peasants(CuTest * tc) { - struct give env = { 0 }; - test_setup_ex(tc); - env.f1 = test_create_faction(0); - env.f2 = 0; - setup_give(&env); - rsetpeasants(env.r, 0); - give_unit(env.src, NULL, NULL); - CuAssertIntEquals(tc, 0, env.src->number); - CuAssertIntEquals(tc, 1, rpeasants(env.r)); - test_cleanup(); -} - static void test_give_unit(CuTest * tc) { struct give env = { 0 }; test_setup_ex(tc); env.f1 = test_create_faction(0); env.f2 = test_create_faction(0); setup_give(&env); - env.r->terrain = test_create_terrain("ocean", SEA_REGION); config_set("rules.give.max_men", "0"); give_unit(env.src, env.dst, NULL); CuAssertPtrEquals(tc, env.f1, env.src->faction); @@ -88,7 +74,38 @@ static void test_give_unit(CuTest * tc) { test_cleanup(); } -static void test_give_unit_in_ocean(CuTest * tc) { +static void test_give_unit_limits(CuTest * tc) { + struct give env = { 0 }; + test_setup_ex(tc); + env.f1 = test_create_faction(0); + env.f2 = test_create_faction(0); + setup_give(&env); + CuAssertIntEquals(tc, 1, env.f1->num_units); + CuAssertIntEquals(tc, 1, env.f2->num_units); + config_set("rules.limit.faction", "1"); + give_unit(env.src, env.dst, NULL); + CuAssertPtrEquals(tc, env.f1, env.src->faction); + CuAssertIntEquals(tc, 0, env.f2->newbies); + CuAssertIntEquals(tc, 1, env.f1->num_units); + CuAssertIntEquals(tc, 1, env.f2->num_units); + CuAssertPtrNotNull(tc, test_find_messagetype(env.f1->msgs, "too_many_units_in_faction")); + test_cleanup(); +} + +static void test_give_unit_to_peasants(CuTest * tc) { + struct give env = { 0 }; + test_setup_ex(tc); + env.f1 = test_create_faction(0); + env.f2 = 0; + setup_give(&env); + rsetpeasants(env.r, 0); + give_unit(env.src, NULL, NULL); + CuAssertIntEquals(tc, 0, env.src->number); + CuAssertIntEquals(tc, 1, rpeasants(env.r)); + test_cleanup(); +} + +static void test_give_unit_to_ocean(CuTest * tc) { struct give env = { 0 }; test_setup_ex(tc); env.f1 = test_create_faction(0); @@ -429,7 +446,8 @@ CuSuite *get_give_suite(void) SUITE_ADD_TEST(suite, test_give_men_requires_contact); SUITE_ADD_TEST(suite, test_give_men_not_to_self); SUITE_ADD_TEST(suite, test_give_unit); - SUITE_ADD_TEST(suite, test_give_unit_in_ocean); + SUITE_ADD_TEST(suite, test_give_unit_limits); + SUITE_ADD_TEST(suite, test_give_unit_to_ocean); SUITE_ADD_TEST(suite, test_give_unit_to_peasants); SUITE_ADD_TEST(suite, test_give_peasants); SUITE_ADD_TEST(suite, test_give_herbs); diff --git a/src/gmtool.c b/src/gmtool.c index 3551c3436..674e139f7 100644 --- a/src/gmtool.c +++ b/src/gmtool.c @@ -26,9 +26,6 @@ #if MUSEUM_MODULE #include #endif -#if ARENA_MODULE -#include -#endif #include #include @@ -905,8 +902,8 @@ static void handlekey(state * st, int c) new_players = read_newfactions(sbuffer); } cnormalize(&st->cursor, &nx, &ny); - minpop = config_get_int("seed.population.min", 8); - maxpop = config_get_int("seed.population.max", minpop); + minpop = config_get_int("editor.population.min", 8); + maxpop = config_get_int("editor.population.max", minpop); if (maxpop > minpop) { n = rng_int() % (maxpop - minpop) + minpop; } diff --git a/src/guard.c b/src/guard.c index 55f189b6c..118ede5fc 100644 --- a/src/guard.c +++ b/src/guard.c @@ -112,7 +112,7 @@ static bool is_guardian_r(const unit * guard) if (guard->building && rule_region_owners() && guard == building_owner(guard->building)) { faction *owner = region_get_owner(guard->region); if (owner == guard->faction) { - building *bowner = largestbuilding(guard->region, &cmp_taxes, false); + building *bowner = largestbuilding(guard->region, cmp_taxes, false); if (bowner == guard->building) { return true; } diff --git a/src/helpers.c b/src/helpers.c index e9502e075..7591dba9f 100644 --- a/src/helpers.c +++ b/src/helpers.c @@ -13,20 +13,25 @@ without prior permission by the authors of Eressea. #include #include "helpers.h" #include "vortex.h" +#include "alchemy.h" #include #include #include +#include #include #include #include #include +#include #include +#include #include #include #include #include +#include #include #include #include @@ -78,7 +83,7 @@ lua_giveitem(unit * s, unit * d, const item_type * itype, int n, struct order *o return result; } -static int limit_resource(const region * r, const resource_type * rtype) +static int limit_resource_lua(const region * r, const resource_type * rtype) { char fname[64]; int result = -1; @@ -110,7 +115,7 @@ static int limit_resource(const region * r, const resource_type * rtype) } static void -produce_resource(region * r, const resource_type * rtype, int norders) +produce_resource_lua(region * r, const resource_type * rtype, int norders) { lua_State *L = (lua_State *)global.vm_state; char fname[64]; @@ -154,23 +159,12 @@ static void push_param(lua_State * L, char c, spllprm * param) } /** callback to use lua for spell functions */ -static int lua_callspell(castorder * co) +static int lua_callspell(castorder * co, const char *fname) { lua_State *L = (lua_State *)global.vm_state; - const char *fname = co->sp->sname; unit *caster = co_get_caster(co); region * r = co_get_region(co); int result = -1; - const char *hashpos = strchr(fname, '#'); - char fbuf[64]; - - if (hashpos != NULL) { - ptrdiff_t len = hashpos - fname; - assert(len < (ptrdiff_t) sizeof(fbuf)); - memcpy(fbuf, fname, len); - fbuf[len] = '\0'; - fname = fbuf; - } lua_getglobal(L, fname); if (lua_isfunction(L, -1)) { @@ -217,43 +211,6 @@ static int lua_callspell(castorder * co) return result; } -/** callback to initialize a familiar from lua. */ -static int lua_initfamiliar(unit * u) -{ - lua_State *L = (lua_State *)global.vm_state; - char fname[64]; - int result = -1; - - strlcpy(fname, "initfamiliar_", sizeof(fname)); - strlcat(fname, u_race(u)->_name, sizeof(fname)); - - lua_getglobal(L, fname); - if (lua_isfunction(L, -1)) { - tolua_pushusertype(L, u, TOLUA_CAST "unit"); - - if (lua_pcall(L, 1, 1, 0) != 0) { - const char *error = lua_tostring(L, -1); - log_error("familiar(%s) calling '%s': %s.\n", unitname(u), fname, error); - lua_pop(L, 1); - } - else { - result = (int)lua_tonumber(L, -1); - lua_pop(L, 1); - } - } - else { - log_warning("familiar(%s) calling '%s': not a function.\n", unitname(u), fname); - lua_pop(L, 1); - } - - create_mage(u, M_GRAY); - - strlcpy(fname, u_race(u)->_name, sizeof(fname)); - strlcat(fname, "_familiar", sizeof(fname)); - equip_unit(u, get_equipment(fname)); - return result; -} - static int lua_changeresource(unit * u, const struct resource_type *rtype, int delta) { @@ -287,219 +244,23 @@ lua_changeresource(unit * u, const struct resource_type *rtype, int delta) return result; } -static int lua_getresource(unit * u, const struct resource_type *rtype) -{ - lua_State *L = (lua_State *)global.vm_state; - int result = -1; - char fname[64]; - - strlcpy(fname, rtype->_name, sizeof(fname)); - strlcat(fname, "_getresource", sizeof(fname)); - - lua_getglobal(L, fname); - if (lua_isfunction(L, -1)) { - tolua_pushusertype(L, u, TOLUA_CAST "unit"); - - if (lua_pcall(L, 1, 1, 0) != 0) { - const char *error = lua_tostring(L, -1); - log_error("get(%s) calling '%s': %s.\n", unitname(u), fname, error); - lua_pop(L, 1); - } - else { - result = (int)lua_tonumber(L, -1); - lua_pop(L, 1); - } - } - else { - log_error("get(%s) calling '%s': not a function.\n", unitname(u), fname); - lua_pop(L, 1); - } - - return result; -} - -static bool lua_canuse_item(const unit * u, const struct item_type *itype) -{ - bool result = true; - lua_State *L = (lua_State *)global.vm_state; - const char *fname = "item_canuse"; - - lua_getglobal(L, fname); - if (lua_isfunction(L, -1)) { - tolua_pushusertype(L, (void *)u, TOLUA_CAST "unit"); - tolua_pushstring(L, itype->rtype->_name); - - if (lua_pcall(L, 2, 1, 0) != 0) { - const char *error = lua_tostring(L, -1); - log_error("use(%s) calling '%s': %s.\n", unitname(u), fname, error); - lua_pop(L, 1); - } - else { - result = lua_toboolean(L, -1); - lua_pop(L, 1); - } - } - else { - log_error("use(%s) calling '%s': not a function.\n", unitname(u), fname); - lua_pop(L, 1); - } - return result; -} - -static int -lua_wage(const region * r, const faction * f, const race * rc, int in_turn) -{ - lua_State *L = (lua_State *)global.vm_state; - const char *fname = "wage"; - int result = -1; - - lua_getglobal(L, fname); - if (lua_isfunction(L, -1)) { - tolua_pushusertype(L, (void *)r, TOLUA_CAST "region"); - tolua_pushusertype(L, (void *)f, TOLUA_CAST "faction"); - tolua_pushstring(L, rc ? rc->_name : 0); - lua_pushinteger(L, in_turn); - - if (lua_pcall(L, 3, 1, 0) != 0) { - const char *error = lua_tostring(L, -1); - log_error("wage(%s) calling '%s': %s.\n", regionname(r, NULL), fname, error); - lua_pop(L, 1); - } - else { - result = (int)lua_tonumber(L, -1); - lua_pop(L, 1); - } - } - else { - log_error("wage(%s) calling '%s': not a function.\n", regionname(r, NULL), fname); - lua_pop(L, 1); - } - - return result; -} - -static void lua_agebuilding(building * b) -{ - lua_State *L = (lua_State *)global.vm_state; - char fname[64]; - - strlcpy(fname, "age_", sizeof(fname)); - strlcat(fname, b->type->_name, sizeof(fname)); - - lua_getglobal(L, fname); - if (lua_isfunction(L, -1)) { - tolua_pushusertype(L, (void *)b, TOLUA_CAST "building"); - - if (lua_pcall(L, 1, 0, 0) != 0) { - const char *error = lua_tostring(L, -1); - log_error("agebuilding(%s) calling '%s': %s.\n", buildingname(b), fname, error); - lua_pop(L, 1); - } - } - else { - log_error("agebuilding(%s) calling '%s': not a function.\n", buildingname(b), fname); - lua_pop(L, 1); - } -} - -static double lua_building_taxes(building * b, int level) -{ - lua_State *L = (lua_State *)global.vm_state; - const char *fname = "building_taxes"; - double result = 0.0F; - - lua_getglobal(L, fname); - if (lua_isfunction(L, -1)) { - tolua_pushusertype(L, (void *)b, TOLUA_CAST "building"); - lua_pushinteger(L, level); - - if (lua_pcall(L, 2, 1, 0) != 0) { - const char *error = lua_tostring(L, -1); - log_error("building_taxes(%s) calling '%s': %s.\n", buildingname(b), fname, error); - lua_pop(L, 1); - } - else { - result = (double)lua_tonumber(L, -1); - lua_pop(L, 1); - } - } - else { - log_error("building_taxes(%s) calling '%s': not a function.\n", buildingname(b), fname); - lua_pop(L, 1); - } - return result; -} - -static int lua_maintenance(const unit * u) -{ - lua_State *L = (lua_State *)global.vm_state; - const char *fname = "maintenance"; - int result = -1; - - lua_getglobal(L, fname); - if (lua_isfunction(L, -1)) { - tolua_pushusertype(L, (void *)u, TOLUA_CAST "unit"); - - if (lua_pcall(L, 1, 1, 0) != 0) { - const char *error = lua_tostring(L, -1); - log_error("maintenance(%s) calling '%s': %s.\n", unitname(u), fname, error); - lua_pop(L, 1); - } - else { - result = (int)lua_tonumber(L, -1); - lua_pop(L, 1); - } - } - else { - log_error("maintenance(%s) calling '%s': not a function.\n", unitname(u), fname); - lua_pop(L, 1); - } - - return result; -} - -static int lua_equipmentcallback(const struct equipment *eq, unit * u) -{ - lua_State *L = (lua_State *)global.vm_state; - char fname[64]; - int result = -1; - - strlcpy(fname, "equip_", sizeof(fname)); - strlcat(fname, eq->name, sizeof(fname)); - - lua_getglobal(L, fname); - if (lua_isfunction(L, -1)) { - tolua_pushusertype(L, (void *)u, TOLUA_CAST "unit"); - - if (lua_pcall(L, 1, 1, 0) != 0) { - const char *error = lua_tostring(L, -1); - log_error("equip(%s) calling '%s': %s.\n", unitname(u), fname, error); - lua_pop(L, 1); - } - else { - result = (int)lua_tonumber(L, -1); - lua_pop(L, 1); - } - } - else { - log_error("equip(%s) calling '%s': not a function.\n", unitname(u), fname); - lua_pop(L, 1); - } - return result; -} - /** callback for an item-use function written in lua. */ -int -lua_useitem(struct unit *u, const struct item_type *itype, int amount, -struct order *ord) +static int +use_item_lua(unit *u, const item_type *itype, int amount, struct order *ord) { lua_State *L = (lua_State *)global.vm_state; int result = 0; char fname[64]; + int (*callout)(unit *, const item_type *, int, struct order *); strlcpy(fname, "use_", sizeof(fname)); strlcat(fname, itype->rtype->_name, sizeof(fname)); + callout = (int(*)(unit *, const item_type *, int, struct order *))get_function(fname); + if (callout) { + return callout(u, itype, amount, ord); + } + lua_getglobal(L, fname); if (lua_isfunction(L, -1)) { tolua_pushusertype(L, (void *)u, TOLUA_CAST "unit"); @@ -515,58 +276,44 @@ struct order *ord) result = (int)lua_tonumber(L, -1); lua_pop(L, 1); } + return result; } - else { - log_error("use(%s) calling '%s': not a function.\n", unitname(u), fname); - lua_pop(L, 1); + lua_pop(L, 1); + if (itype->rtype->ptype) { + return use_potion(u, itype, amount, ord); + } else { + log_error("no such callout: %s", fname); } + log_error("use(%s) calling '%s': not a function.\n", unitname(u), fname); return result; } -int tolua_toid(lua_State * L, int idx, int def) +/* compat code for old data files */ +static int caldera_read(trigger *t, struct gamedata *data) { - int no = 0; - int type = lua_type(L, idx); - if (type == LUA_TNUMBER) { - no = (int)tolua_tonumber(L, idx, def); - } - else { - const char *str = tolua_tostring(L, idx, NULL); - no = str ? atoi36(str) : def; - } - return no; + UNUSED_ARG(t); + read_building_reference(data); + return AT_READ_FAIL; } +struct trigger_type tt_caldera = { + "caldera", + NULL, NULL, NULL, NULL, + caldera_read +}; + void register_tolua_helpers(void) { + tt_register(&tt_caldera); at_register(&at_direction); at_register(&at_building_action); - register_function((pf_generic)lua_building_taxes, - TOLUA_CAST "lua_building_taxes"); - register_function((pf_generic)lua_agebuilding, - TOLUA_CAST "lua_agebuilding"); - register_function((pf_generic)lua_callspell, TOLUA_CAST "lua_castspell"); - register_function((pf_generic)lua_initfamiliar, - TOLUA_CAST "lua_initfamiliar"); - register_item_use(&lua_useitem, TOLUA_CAST "lua_useitem"); - register_function((pf_generic)lua_getresource, - TOLUA_CAST "lua_getresource"); - register_function((pf_generic)lua_canuse_item, - TOLUA_CAST "lua_canuse_item"); - register_function((pf_generic)lua_changeresource, - TOLUA_CAST "lua_changeresource"); - register_function((pf_generic)lua_equipmentcallback, - TOLUA_CAST "lua_equip"); + callbacks.cast_spell = lua_callspell; + callbacks.use_item = use_item_lua; + callbacks.produce_resource = produce_resource_lua; + callbacks.limit_resource = limit_resource_lua; - register_function((pf_generic)lua_wage, TOLUA_CAST "lua_wage"); - register_function((pf_generic)lua_maintenance, - TOLUA_CAST "lua_maintenance"); - - register_function((pf_generic)produce_resource, - TOLUA_CAST "lua_produceresource"); - register_function((pf_generic)limit_resource, - TOLUA_CAST "lua_limitresource"); - register_item_give(lua_giveitem, TOLUA_CAST "lua_giveitem"); + register_function((pf_generic)lua_changeresource, "lua_changeresource"); + register_item_give(lua_giveitem, "lua_giveitem"); } diff --git a/src/helpers.h b/src/helpers.h index d77765187..d0bbcc58b 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -14,9 +14,9 @@ without prior permission by the authors of Eressea. extern "C" { #endif - struct lua_State; - void register_tolua_helpers(void); - int tolua_toid(struct lua_State *L, int idx, int def); + struct lua_State; + + void register_tolua_helpers(void); #ifdef __cplusplus } diff --git a/src/items.c b/src/items.c index bb5009038..0f97ea675 100644 --- a/src/items.c +++ b/src/items.c @@ -1,6 +1,7 @@ #include #include "items.h" +#include "alchemy.h" #include "study.h" #include "economy.h" #include "move.h" @@ -14,16 +15,24 @@ #include #include #include +#include #include #include #include #include -#include +#include + +/* triggers includes */ +#include +#include #include +#include +#include #include #include +#include #include #include @@ -34,7 +43,7 @@ static int use_studypotion(struct unit *u, const struct item_type *itype, int amount, struct order *ord) { - if (init_order(u->thisorder) == K_STUDY) { + if (u->thisorder && init_order(u->thisorder) == K_STUDY) { char token[128]; skill_t sk = NOSKILL; skill *sv = 0; @@ -61,9 +70,9 @@ struct order *ord) if (amount > MAXGAIN) { amount = MAXGAIN; } - teach->value += amount * 30; - if (teach->value > MAXGAIN * 30) { - teach->value = MAXGAIN * 30; + teach->value += amount * STUDYDAYS; + if (teach->value > MAXGAIN * STUDYDAYS) { + teach->value = MAXGAIN * STUDYDAYS; } i_change(&u->items, itype, -amount); return 0; @@ -71,7 +80,6 @@ struct order *ord) } return EUNUSABLE; } - /* END studypotion */ /* BEGIN speedsail */ @@ -89,9 +97,8 @@ struct order *ord) } effect = SPEEDSAIL_EFFECT; - c = - create_curse(u, &sh->attribs, ct_find("shipspeedup"), 20, INT_MAX, effect, - 0); + c = create_curse(u, &sh->attribs, ct_find("shipspeedup"), 20, INT_MAX, + effect, 0); c_setflag(c, CURSE_NOAGE); ADDMSG(&u->faction->msgs, msg_message("use_speedsail", "unit speed", u, @@ -125,15 +132,15 @@ struct order *ord) UNUSED_ARG(ord); assert(sp); - /* Reduziert die Strke jedes Spruchs um effect */ + /* Reduziert die St�rke jedes Spruchs um effect */ effect = 5; - /* Hlt Sprche bis zu einem summierten Gesamtlevel von power aus. + /* H�lt Spr�che bis zu einem summierten Gesamtlevel von power aus. * Jeder Zauber reduziert die 'Lebenskraft' (vigour) der Antimagiezone * um seine Stufe */ force = effect * 20; /* Stufe 5 =~ 100 */ - /* Regionszauber auflsen */ + /* Regionszauber aufl�sen */ while (*ap && force > 0) { curse *c; attrib *a = *ap; @@ -145,7 +152,7 @@ struct order *ord) } c = (curse *)a->data.v; - /* Immunitt prfen */ + /* Immunit�t pr�fen */ if (c_flags(c) & CURSE_IMMUNE) { do { ap = &(*ap)->next; @@ -171,52 +178,8 @@ struct order *ord) return 0; } -static int -use_instantartsculpture(struct unit *u, const struct item_type *itype, -int amount, struct order *ord) -{ - building *b; - - if (u->region->land == NULL) { - ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_onlandonly", "")); - return -1; - } - - b = new_building(bt_find("artsculpture"), u->region, u->faction->locale); - b->size = 100; - - ADDMSG(&u->region->msgs, msg_message("artsculpture_create", "unit region", - u, u->region)); - - use_pooled(u, itype->rtype, GET_DEFAULT, 1); - - return 0; -} - -static int -use_instantartacademy(struct unit *u, const struct item_type *itype, -int amount, struct order *ord) -{ - building *b; - - if (u->region->land == NULL) { - ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_onlandonly", "")); - return -1; - } - - b = new_building(bt_find("artacademy"), u->region, u->faction->locale); - b->size = 100; - - ADDMSG(&u->region->msgs, msg_message("artacademy_create", "unit region", u, - u->region)); - - use_pooled(u, itype->rtype, GET_DEFAULT, 1); - - return 0; -} - -#define BAGPIPEFRACTION dice_rand("2d4+2") -#define BAGPIPEDURATION dice_rand("2d10+4") +#define BAGPIPEFRACTION (dice(2,4)+2) +#define BAGPIPEDURATION (dice(2,10)+4) static int use_bagpipeoffear(struct unit *u, const struct item_type *itype, @@ -264,14 +227,204 @@ int amount, struct order *ord) return 0; } +static int +use_birthdayamulet(unit * u, const struct item_type *itype, int amount, +struct order *ord) +{ + direction_t d; + message *msg = msg_message("meow", ""); + + UNUSED_ARG(ord); + UNUSED_ARG(amount); + UNUSED_ARG(itype); + + add_message(&u->region->msgs, msg); + for (d = 0; d < MAXDIRECTIONS; d++) { + region *tr = rconnect(u->region, d); + if (tr) + add_message(&tr->msgs, msg); + } + msg_release(msg); + return 0; +} + +static int use_foolpotion(unit *u, const item_type *itype, int amount, + struct order *ord) +{ + int targetno = read_unitid(u->faction, u->region); + unit *target = findunit(targetno); + if (target == NULL || u->region != target->region) { + ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found", + "")); + return ECUSTOM; + } + if (effskill(u, SK_STEALTH, 0) <= effskill(target, SK_PERCEPTION, 0)) { + cmistake(u, ord, 64, MSG_EVENT); + return ECUSTOM; + } + ADDMSG(&u->faction->msgs, msg_message("givedumb", + "unit recipient amount", u, target, amount)); + + change_effect(target, itype->rtype->ptype, amount); + use_pooled(u, itype->rtype, GET_DEFAULT, amount); + return 0; +} + +static int +use_bloodpotion(struct unit *u, const struct item_type *itype, int amount, +struct order *ord) +{ + if (u->number == 0 || u_race(u) == get_race(RC_DAEMON)) { + change_effect(u, itype->rtype->ptype, 100 * amount); + } + else { + const race *irace = u_irace(u); + if (irace == u_race(u)) { + const race *rcfailure = rc_find("smurf"); + if (!rcfailure) { + rcfailure = rc_find("toad"); + } + if (rcfailure) { + trigger *trestore = trigger_changerace(u, u_race(u), irace); + if (trestore) { + int duration = 2 + rng_int() % 8; + + add_trigger(&u->attribs, "timer", trigger_timeout(duration, + trestore)); + u->irace = NULL; + u_setrace(u, rcfailure); + } + } + } + } + use_pooled(u, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK, + amount); + usetpotionuse(u, itype->rtype->ptype); + + ADDMSG(&u->faction->msgs, msg_message("usepotion", + "unit potion", u, itype->rtype)); + return 0; +} + +static int heal(unit * user, int effect) +{ + int req = unit_max_hp(user) * user->number - user->hp; + if (req > 0) { + req = MIN(req, effect); + effect -= req; + user->hp += req; + } + return effect; +} + +static int +use_healingpotion(struct unit *user, const struct item_type *itype, int amount, +struct order *ord) +{ + int effect = amount * 400; + unit *u = user->region->units; + effect = heal(user, effect); + while (effect > 0 && u != NULL) { + if (u->faction == user->faction) { + effect = heal(u, effect); + } + u = u->next; + } + use_pooled(user, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK, + amount); + usetpotionuse(user, itype->rtype->ptype); + + ADDMSG(&user->faction->msgs, msg_message("usepotion", + "unit potion", user, itype->rtype)); + return 0; +} + +/* ------------------------------------------------------------- */ +/* Kann auch von Nichtmagier benutzt werden, modifiziert Taktik fuer diese +* Runde um -1 - 4 Punkte. */ +static int +use_tacticcrystal(unit * u, const struct item_type *itype, int amount, + struct order *ord) +{ + int i; + for (i = 0; i != amount; ++i) { + int duration = 1; /* wirkt nur in dieser Runde */ + curse *c; + float effect; + float power = 5; /* Widerstand gegen Antimagiesprueche, ist in diesem + Fall egal, da der curse fuer den Kampf gelten soll, + der vor den Antimagiezaubern passiert */ + + effect = (float)(rng_int() % 6 - 1); + c = create_curse(u, &u->attribs, ct_find("skillmod"), power, + duration, effect, u->number); + c->data.i = SK_TACTICS; + UNUSED_ARG(ord); + } + use_pooled(u, itype->rtype, GET_DEFAULT, amount); + ADDMSG(&u->faction->msgs, msg_message("use_tacticcrystal", + "unit region", u, u->region)); + return 0; +} + +static int +use_mistletoe(struct unit *user, const struct item_type *itype, int amount, + struct order *ord) +{ + int mtoes = + get_pooled(user, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK, + user->number); + + if (user->number > mtoes) { + ADDMSG(&user->faction->msgs, msg_message("use_singleperson", + "unit item region command", user, itype->rtype, user->region, ord)); + return -1; + } + use_pooled(user, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK, + user->number); + a_add(&user->attribs, make_fleechance((float)1.0)); + ADDMSG(&user->faction->msgs, + msg_message("use_item", "unit item", user, itype->rtype)); + + return 0; +} + +static int use_warmthpotion(unit *u, const item_type *itype, + int amount, struct order *ord) +{ + if (u->faction->race == get_race(RC_INSECT)) { + u->flags |= UFL_WARMTH; + } + else { + /* nur fuer insekten: */ + cmistake(u, ord, 163, MSG_EVENT); + return ECUSTOM; + } + use_pooled(u, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK, + amount); + usetpotionuse(u, itype->rtype->ptype); + + ADDMSG(&u->faction->msgs, msg_message("usepotion", + "unit potion", u, itype->rtype)); + return 0; +} + void register_itemfunctions(void) { - register_demonseye(); - register_item_use(use_antimagiccrystal, "use_antimagiccrystal"); - register_item_use(use_instantartsculpture, "use_instantartsculpture"); + /* have tests: */ + register_item_use(use_mistletoe, "use_mistletoe"); + register_item_use(use_tacticcrystal, "use_dreameye"); register_item_use(use_studypotion, "use_studypotion"); + register_item_use(use_antimagiccrystal, "use_antimagic"); register_item_use(use_speedsail, "use_speedsail"); - register_item_use(use_instantartacademy, "use_instantartacademy"); register_item_use(use_bagpipeoffear, "use_bagpipeoffear"); register_item_use(use_aurapotion50, "use_aurapotion50"); + register_item_use(use_birthdayamulet, "use_aoc"); + register_item_use(use_foolpotion, "use_p7"); + register_item_use(use_bloodpotion, "use_peasantblood"); + register_item_use(use_healingpotion, "use_ointment"); + register_item_use(use_warmthpotion, "use_nestwarmth"); + + /* ungetestet: Wasser des Lebens */ + register_item_use(use_potion_delayed, "use_p2"); } diff --git a/src/items/CMakeLists.txt b/src/items/CMakeLists.txt index bbf192a66..37777f9e4 100644 --- a/src/items/CMakeLists.txt +++ b/src/items/CMakeLists.txt @@ -5,7 +5,6 @@ xerewards.test.c ) SET(_FILES -demonseye.c speedsail.c weapons.c xerewards.c diff --git a/src/items/demonseye.c b/src/items/demonseye.c deleted file mode 100644 index 563a16047..000000000 --- a/src/items/demonseye.c +++ /dev/null @@ -1,65 +0,0 @@ -/* -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. -**/ - -#include -#include "demonseye.h" - -/* kernel includes */ -#include -#include -#include -#include -#include -#include - -/* util includes */ -#include - -/* libc includes */ -#include - -static int -summon_igjarjuk(struct unit *u, const struct item_type *itype, int amount, -struct order *ord) -{ - struct plane *p = rplane(u->region); - UNUSED_ARG(amount); - UNUSED_ARG(itype); - if (p != NULL) { - ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "use_realworld_only", "")); - return EUNUSABLE; - } - else { - assert(!"not implemented"); - return EUNUSABLE; - } -} - -static int -give_igjarjuk(struct unit *src, struct unit *d, const struct item_type *itype, -int n, struct order *ord) -{ - ADDMSG(&src->faction->msgs, msg_feedback(src, ord, "error_giveeye", "")); - return 0; -} - -void register_demonseye(void) -{ - register_item_use(summon_igjarjuk, "useigjarjuk"); - register_item_give(give_igjarjuk, "giveigjarjuk"); -} diff --git a/src/items/demonseye.h b/src/items/demonseye.h deleted file mode 100644 index 8d878a174..000000000 --- a/src/items/demonseye.h +++ /dev/null @@ -1,30 +0,0 @@ -/* -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. -**/ - -#ifndef H_ITM_DEMONSEYE -#define H_ITM_DEMONSEYE -#ifdef __cplusplus -extern "C" { -#endif - - void register_demonseye(void); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/kernel/CMakeLists.txt b/src/kernel/CMakeLists.txt index d3a2e8e56..fe560af7a 100644 --- a/src/kernel/CMakeLists.txt +++ b/src/kernel/CMakeLists.txt @@ -6,6 +6,7 @@ alliance.test.c ally.test.c build.test.c building.test.c +# callbacks.test.c command.test.c config.test.c # connection.test.c @@ -16,7 +17,7 @@ group.test.c item.test.c messages.test.c order.test.c -# pathdinder.test.c +# pathfinder.test.c plane.test.c pool.test.c race.test.c @@ -38,6 +39,7 @@ alliance.c ally.c build.c building.c +callbacks.c command.c config.c connection.c @@ -54,6 +56,7 @@ pool.c race.c region.c resources.c +rules.c save.c ship.c skills.c diff --git a/src/kernel/alliance.c b/src/kernel/alliance.c index 0feb8407d..910670d10 100644 --- a/src/kernel/alliance.c +++ b/src/kernel/alliance.c @@ -239,8 +239,25 @@ static void perform_transfer(void) } } +bool num_units_cb(void *entry, void *more) { + faction *f = (faction *)entry; + int *num = (int *)more; + *num += f->num_units; + return true; +} + +int alliance_size(const alliance *al) +{ + int num = 0; + if (al) { + selist_foreach_ex(al->members, num_units_cb, &num); + } + return num; +} + static void perform_join(void) { + int alimit = rule_alliance_limit(); alliance_transaction **tap = transactions + ALLIANCE_JOIN; while (*tap) { alliance_transaction *ta = *tap; @@ -270,7 +287,16 @@ static void perform_join(void) ti = *tip; } if (ti) { - setalliance(fj, al); + int maxsize = (alimit > 0) ? (alimit - alliance_size(al)) : 0; + if (alimit > 0 && fj->num_units > maxsize) { + ADDMSG(&fj->msgs, + msg_feedback(ta->u, ta->ord, + "too_many_units_in_alliance", + "allowed", alimit)); + } + else { + setalliance(fj, al); + } *tip = ti->next; free(ti); } diff --git a/src/kernel/alliance.h b/src/kernel/alliance.h index 8e112e8c9..00b4b29e4 100644 --- a/src/kernel/alliance.h +++ b/src/kernel/alliance.h @@ -67,7 +67,7 @@ extern "C" { struct faction *alliance_get_leader(struct alliance *al); void alliance_cmd(void); bool is_allied(const struct faction *f1, const struct faction *f2); - + int alliance_size(const struct alliance *al); /* #units in the alliance */ void alliance_setname(alliance * self, const char *name); /* execute commands */ diff --git a/src/kernel/alliance.test.c b/src/kernel/alliance.test.c index f63767672..885bc8413 100644 --- a/src/kernel/alliance.test.c +++ b/src/kernel/alliance.test.c @@ -112,6 +112,29 @@ static void test_alliance_cmd(CuTest *tc) { test_cleanup(); } +static void test_alliance_limits(CuTest *tc) { + unit *u1, *u2; + struct region *r; + + test_setup(); + r = test_create_region(0, 0, 0); + u1 = test_create_unit(test_create_faction(0), r); + u2 = test_create_unit(test_create_faction(0), r); + + config_set("rules.limit.alliance", "1"); + unit_addorder(u1, create_order(K_ALLIANCE, u1->faction->locale, "%s %s", alliance_kwd[ALLIANCE_NEW], itoa36(42))); + unit_addorder(u1, create_order(K_ALLIANCE, u1->faction->locale, "%s %s", alliance_kwd[ALLIANCE_INVITE], itoa36(u2->faction->no))); + unit_addorder(u2, create_order(K_ALLIANCE, u1->faction->locale, "%s %s", alliance_kwd[ALLIANCE_JOIN], itoa36(42))); + CuAssertTrue(tc, !is_allied(u1->faction, u2->faction)); + CuAssertPtrEquals(tc, 0, f_get_alliance(u1->faction)); + alliance_cmd(); + CuAssertPtrNotNull(tc, f_get_alliance(u1->faction)); + CuAssertPtrEquals(tc, 0, f_get_alliance(u2->faction)); + CuAssertTrue(tc, !is_allied(u1->faction, u2->faction)); + CuAssertPtrNotNull(tc, test_find_messagetype(u2->faction->msgs, "too_many_units_in_alliance")); + test_cleanup(); +} + static void test_alliance_cmd_kick(CuTest *tc) { unit *u1, *u2; struct region *r; @@ -200,6 +223,7 @@ CuSuite *get_alliance_suite(void) SUITE_ADD_TEST(suite, test_alliance_dead_faction); SUITE_ADD_TEST(suite, test_alliance_make); SUITE_ADD_TEST(suite, test_alliance_join); + SUITE_ADD_TEST(suite, test_alliance_limits); SUITE_ADD_TEST(suite, test_alliance_cmd); SUITE_ADD_TEST(suite, test_alliance_cmd_no_invite); SUITE_ADD_TEST(suite, test_alliance_cmd_kick); diff --git a/src/kernel/build.c b/src/kernel/build.c index 88ab35fd2..57c172905 100644 --- a/src/kernel/build.c +++ b/src/kernel/build.c @@ -43,6 +43,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include #include #include @@ -67,9 +68,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include -/* attributes inclues */ -#include - struct building *getbuilding(const struct region *r) { building *b = findbuilding(getid()); @@ -404,16 +402,26 @@ static int required(int size, int msize, int maxneed) return used; } -static int -matmod(const attrib * a, const unit * u, const resource_type * material, -int value) +static int matmod(const unit * u, const resource_type * rtype, int value) { - for (a = a_find((attrib *)a, &at_matmod); a && a->type == &at_matmod; - a = a->next) { - mm_fun fun = (mm_fun)a->data.f; - value = fun(u, material, value); - if (value < 0) - return value; /* pass errors to caller */ + if (rtype->modifiers) { + variant save = frac_make(1, 1); + const struct building_type *btype = NULL; + const struct race *rc = u_race(u); + resource_mod *mod; + if (u->building && inside_building(u)) { + btype = u->building->type; + } + for (mod = rtype->modifiers; mod->type != RMT_END; ++mod) { + if (mod->type == RMT_USE_SAVE) { + if (!mod->btype || mod->btype == btype) { + if (!mod->race || mod->race == rc) { + save = frac_mul(save, mod->value); + } + } + } + } + return value * save.sa[0] / save.sa[1]; } return value; } @@ -439,14 +447,8 @@ static int use_materials(unit *u, const construction *type, int n, int completed required(completed + n, type->reqsize, type->materials[c].number); int multi = 1; int canuse = 100; /* normalization */ - if (building_is_active(u->building) && inside_building(u)) { - canuse = matmod(u->building->type->attribs, u, rtype, canuse); - } - if (canuse < 0) { - return canuse; /* pass errors to caller */ - } - canuse = matmod(type->attribs, u, rtype, canuse); - + canuse = matmod(u, rtype, canuse); + assert(canuse >= 0); assert(canuse % 100 == 0 || !"only constant multipliers are implemented in build()"); multi = canuse / 100; @@ -468,14 +470,9 @@ static int count_materials(unit *u, const construction *type, int n, int complet const struct resource_type *rtype = type->materials[c].rtype; int need, prebuilt; int canuse = get_pooled(u, rtype, GET_DEFAULT, INT_MAX); + canuse = matmod(u, rtype, canuse); - if (building_is_active(u->building) && inside_building(u)) { - canuse = matmod(u->building->type->attribs, u, rtype, canuse); - } - - if (canuse < 0) - return canuse; /* pass errors to caller */ - canuse = matmod(type->attribs, u, rtype, canuse); + assert(canuse >= 0); if (type->reqsize > 1) { prebuilt = required(completed, type->reqsize, type->materials[c].number); @@ -503,50 +500,30 @@ static int count_materials(unit *u, const construction *type, int n, int complet * of the first object have already been finished. return the * actual size that could be built. */ -int build(unit * u, const construction * ctype, int completed, int want) +int build(unit * u, const construction * ctype, int completed, int want, int skill_mod) { - const construction *type = ctype; + const construction *con = ctype; int skills = INT_MAX; /* number of skill points remainig */ int basesk = 0; int made = 0; if (want <= 0) return 0; - if (type == NULL) + if (con == NULL) return ENOMATERIALS; - if (type->improvement == NULL && completed == type->maxsize) + if (con->improvement == NULL && completed == con->maxsize) return ECOMPLETE; - if (type->btype != NULL) { - building *b; - if (!u->building || u->building->type != type->btype) { - return EBUILDINGREQ; - } - b = inside_building(u); - if (!b || !building_is_active(b)) { - return EBUILDINGREQ; - } - } - - if (type->skill != NOSKILL) { + if (con->skill != NOSKILL) { int effsk; int dm = get_effect(u, oldpotiontype[P_DOMORE]); assert(u->number); - basesk = effskill(u, type->skill, 0); + basesk = effskill(u, con->skill, 0); if (basesk == 0) return ENEEDSKILL; - effsk = basesk; - if (building_is_active(u->building) && inside_building(u)) { - effsk = skillmod(u->building->type->attribs, u, u->region, type->skill, - effsk, SMF_PRODUCTION); - } - effsk = skillmod(type->attribs, u, u->region, type->skill, - effsk, SMF_PRODUCTION); - if (effsk < 0) - return effsk; /* pass errors to caller */ - if (effsk == 0) - return ENEEDSKILL; + effsk = basesk + skill_mod; + assert(effsk >= 0); skills = effsk * u->number; @@ -569,13 +546,13 @@ int build(unit * u, const construction * ctype, int completed, int want) * type->improvement==type means build another object of the same time * while material lasts type->improvement==x means build x when type * is finished */ - while (type && type->improvement && - type->improvement != type && - type->maxsize > 0 && type->maxsize <= completed) { - completed -= type->maxsize; - type = type->improvement; + while (con && con->improvement && + con->improvement != con && + con->maxsize > 0 && con->maxsize <= completed) { + completed -= con->maxsize; + con = con->improvement; } - if (type == NULL) { + if (con == NULL) { if (made == 0) return ECOMPLETE; break; /* completed */ @@ -586,15 +563,15 @@ int build(unit * u, const construction * ctype, int completed, int want) * (enno): Nein, das ist f�r Dinge, bei denen die n�chste Ausbaustufe * die gleiche wie die vorherige ist. z.b. gegenst�nde. */ - if (type->maxsize > 0) { - completed = completed % type->maxsize; + if (con->maxsize > 0) { + completed = completed % con->maxsize; } else { completed = 0; - assert(type->reqsize >= 1); + assert(con->reqsize >= 1); } - if (basesk < type->minskill) { + if (basesk < con->minskill) { if (made == 0) return ELOWSKILL; else @@ -602,15 +579,15 @@ int build(unit * u, const construction * ctype, int completed, int want) } /* n = maximum buildable size */ - if (type->minskill > 1) { - n = skills / type->minskill; + if (con->minskill > 1) { + n = skills / con->minskill; } else { n = skills; } /* Flinkfingerring wirkt nicht auf Mengenbegrenzte (magische) * Talente */ - if (skill_limit(u->faction, type->skill) == INT_MAX) { + if (skill_limit(u->faction, con->skill) == INT_MAX) { const resource_type *ring = get_resourcetype(R_RING_OF_NIMBLEFINGER); item *itm = ring ? *i_find(&u->items, ring->itype) : 0; int i = itm ? itm->number : 0; @@ -622,26 +599,26 @@ int build(unit * u, const construction * ctype, int completed, int want) if (want < n) n = want; - if (type->maxsize > 0) { - n = MIN(type->maxsize - completed, n); - if (type->improvement == NULL) { + if (con->maxsize > 0) { + n = MIN(con->maxsize - completed, n); + if (con->improvement == NULL) { want = n; } } - n = count_materials(u, type, n, completed); + n = count_materials(u, con, n, completed); if (n <= 0) { if (made == 0) return ENOMATERIALS; else break; } - err = use_materials(u, type, n, completed); + err = use_materials(u, con, n, completed); if (err < 0) { return err; } made += n; - skills -= n * type->minskill; + skills -= n * con->minskill; want -= n; completed = completed + n; } @@ -799,7 +776,7 @@ build_building(unit * u, const building_type * btype, int id, int want, order * } } } - built = build(u, btype->construction, built, n); + built = build(u, btype->construction, built, n, 0); switch (built) { case ECOMPLETE: @@ -884,7 +861,7 @@ static void build_ship(unit * u, ship * sh, int want) const construction *construction = sh->type->construction; int size = (sh->size * DAMAGE_SCALE - sh->damage) / DAMAGE_SCALE; int n; - int can = build(u, construction, size, want); + int can = build(u, construction, size, want, 0); if ((n = construction->maxsize - sh->size) > 0 && can > 0) { if (can >= n) { diff --git a/src/kernel/build.h b/src/kernel/build.h index 7780a6cd2..c201d900b 100644 --- a/src/kernel/build.h +++ b/src/kernel/build.h @@ -32,39 +32,35 @@ extern "C" { * wichtig */ - struct xml_tag; - typedef struct requirement { const struct resource_type *rtype; int number; } requirement; + typedef enum construct_t { + CONS_OTHER, + CONS_ITEM, + CONS_BUILDING + } construct_t; + typedef struct construction { + construct_t type; skill_t skill; /* skill req'd per point of size */ int minskill; /* skill req'd per point of size */ int maxsize; /* maximum size of this type */ int reqsize; /* size of object using up 1 set of requirement. */ requirement *materials; /* material req'd to build one object */ - const struct building_type *btype; - /* building type required to make this thing */ + /* only used by CONS_BUILDING: */ + char * name; /* building level name */ struct construction *improvement; - /* next level, if upgradable. if more than one of these items - * can be built (weapons, armour) per turn, must not be NULL, - * but point to the same type again: - * const_sword.improvement = &const_sword - * last level of a building points to NULL, as do objects of - * an unlimited size. - */ - struct attrib *attribs; - /* stores skill modifiers and other attributes */ - + /* next level, if upgradable. */ } construction; void free_construction(struct construction *cons); - extern int destroy_cmd(struct unit *u, struct order *ord); - extern int leave_cmd(struct unit *u, struct order *ord); + int destroy_cmd(struct unit *u, struct order *ord); + int leave_cmd(struct unit *u, struct order *ord); void build_road(struct unit *u, int size, direction_t d); void create_ship(struct unit *u, const struct ship_type *newtype, @@ -74,22 +70,20 @@ extern "C" { struct building *getbuilding(const struct region *r); struct ship *getship(const struct region *r); - void reportevent(struct region *r, char *s); - void shash(struct ship *sh); void sunhash(struct ship *sh); - extern int roqf_factor(void); + int roqf_factor(void); + + int build(struct unit *u, const construction * ctype, int completed, int want, int skill_mod); + int maxbuild(const struct unit *u, const construction * cons); + struct message *msg_materials_required(struct unit *u, struct order *ord, + const struct construction *ctype, int multi); - int build(struct unit *u, const construction * ctype, int completed, int want); - extern int maxbuild(const struct unit *u, const construction * cons); - extern struct message *msg_materials_required(struct unit *u, - struct order *ord, const struct construction *ctype, int multi); /** error messages that build may return: */ #define ELOWSKILL -1 #define ENEEDSKILL -2 #define ECOMPLETE -3 #define ENOMATERIALS -4 -#define EBUILDINGREQ -5 #ifdef __cplusplus } diff --git a/src/kernel/build.test.c b/src/kernel/build.test.c index 77efcfd73..8cd408b09 100644 --- a/src/kernel/build.test.c +++ b/src/kernel/build.test.c @@ -63,36 +63,14 @@ static void test_build_requires_materials(CuTest *tc) { u = setup_build(&bf); set_level(u, SK_ARMORER, 2); - CuAssertIntEquals(tc, ENOMATERIALS, build(u, &bf.cons, 0, 1)); + CuAssertIntEquals(tc, ENOMATERIALS, build(u, &bf.cons, 0, 1, 0)); itype = bf.cons.materials[0].rtype->itype; i_change(&u->items, itype, 2); - CuAssertIntEquals(tc, 1, build(u, &bf.cons, 0, 1)); + CuAssertIntEquals(tc, 1, build(u, &bf.cons, 0, 1, 0)); CuAssertIntEquals(tc, 1, i_get(u->items, itype)); teardown_build(&bf); } -static void test_build_requires_building(CuTest *tc) { - build_fixture bf = { 0 }; - unit *u; - const struct resource_type *rtype; - building_type *btype; - - u = setup_build(&bf); - rtype = bf.cons.materials[0].rtype; - i_change(&u->items, rtype->itype, 1); - set_level(u, SK_ARMORER, 2); - bf.cons.btype = btype = bt_get_or_create("hodor"); - btype->maxcapacity = 1; - btype->capacity = 1; - CuAssertIntEquals_Msg(tc, "must be inside a production building", EBUILDINGREQ, build(u, &bf.cons, 0, 1)); - u->building = test_create_building(u->region, btype); - fset(u->building, BLD_MAINTAINED); - CuAssertIntEquals(tc, 1, build(u, &bf.cons, 0, 1)); - btype->maxcapacity = 0; - CuAssertIntEquals_Msg(tc, "cannot build when production building capacity exceeded", EBUILDINGREQ, build(u, &bf.cons, 0, 1)); - teardown_build(&bf); -} - static void test_build_failure_missing_skill(CuTest *tc) { build_fixture bf = { 0 }; unit *u; @@ -101,7 +79,7 @@ static void test_build_failure_missing_skill(CuTest *tc) { u = setup_build(&bf); rtype = bf.cons.materials[0].rtype; i_change(&u->items, rtype->itype, 1); - CuAssertIntEquals(tc, ENEEDSKILL, build(u, &bf.cons, 1, 1)); + CuAssertIntEquals(tc, ENEEDSKILL, build(u, &bf.cons, 1, 1, 0)); teardown_build(&bf); } @@ -114,7 +92,7 @@ static void test_build_failure_low_skill(CuTest *tc) { rtype = bf.cons.materials[0].rtype; i_change(&u->items, rtype->itype, 1); set_level(u, SK_ARMORER, bf.cons.minskill - 1); - CuAssertIntEquals(tc, ELOWSKILL, build(u, &bf.cons, 0, 10)); + CuAssertIntEquals(tc, ELOWSKILL, build(u, &bf.cons, 0, 10, 0)); teardown_build(&bf); } @@ -128,7 +106,7 @@ static void test_build_failure_completed(CuTest *tc) { i_change(&u->items, rtype->itype, 1); set_level(u, SK_ARMORER, bf.cons.minskill); bf.cons.maxsize = 1; - CuAssertIntEquals(tc, ECOMPLETE, build(u, &bf.cons, bf.cons.maxsize, 10)); + CuAssertIntEquals(tc, ECOMPLETE, build(u, &bf.cons, bf.cons.maxsize, 10, 0)); CuAssertIntEquals(tc, 1, i_get(u->items, rtype->itype)); teardown_build(&bf); } @@ -143,19 +121,19 @@ static void test_build_limits(CuTest *tc) { assert(rtype); i_change(&u->items, rtype->itype, 1); set_level(u, SK_ARMORER, bf.cons.minskill); - CuAssertIntEquals(tc, 1, build(u, &bf.cons, 0, 10)); + CuAssertIntEquals(tc, 1, build(u, &bf.cons, 0, 10, 0)); CuAssertIntEquals(tc, 0, i_get(u->items, rtype->itype)); scale_number(u, 2); set_level(u, SK_ARMORER, bf.cons.minskill); i_change(&u->items, rtype->itype, 2); - CuAssertIntEquals(tc, 2, build(u, &bf.cons, 0, 10)); + CuAssertIntEquals(tc, 2, build(u, &bf.cons, 0, 10, 0)); CuAssertIntEquals(tc, 0, i_get(u->items, rtype->itype)); scale_number(u, 2); set_level(u, SK_ARMORER, bf.cons.minskill * 2); i_change(&u->items, rtype->itype, 4); - CuAssertIntEquals(tc, 4, build(u, &bf.cons, 0, 10)); + CuAssertIntEquals(tc, 4, build(u, &bf.cons, 0, 10, 0)); CuAssertIntEquals(tc, 0, i_get(u->items, rtype->itype)); teardown_build(&bf); } @@ -174,7 +152,7 @@ static void test_build_with_ring(CuTest *tc) { set_level(u, SK_ARMORER, bf.cons.minskill); i_change(&u->items, rtype->itype, 20); i_change(&u->items, ring, 1); - CuAssertIntEquals(tc, 10, build(u, &bf.cons, 0, 20)); + CuAssertIntEquals(tc, 10, build(u, &bf.cons, 0, 20, 0)); CuAssertIntEquals(tc, 10, i_get(u->items, rtype->itype)); teardown_build(&bf); } @@ -193,16 +171,16 @@ static void test_build_with_potion(CuTest *tc) { i_change(&u->items, rtype->itype, 20); change_effect(u, ptype, 4); set_level(u, SK_ARMORER, bf.cons.minskill); - CuAssertIntEquals(tc, 2, build(u, &bf.cons, 0, 20)); + CuAssertIntEquals(tc, 2, build(u, &bf.cons, 0, 20, 0)); CuAssertIntEquals(tc, 18, i_get(u->items, rtype->itype)); CuAssertIntEquals(tc, 3, get_effect(u, ptype)); set_level(u, SK_ARMORER, bf.cons.minskill * 2); - CuAssertIntEquals(tc, 4, build(u, &bf.cons, 0, 20)); + CuAssertIntEquals(tc, 4, build(u, &bf.cons, 0, 20, 0)); CuAssertIntEquals(tc, 2, get_effect(u, ptype)); set_level(u, SK_ARMORER, bf.cons.minskill); scale_number(u, 2); /* OBS: this scales the effects, too: */ CuAssertIntEquals(tc, 4, get_effect(u, ptype)); - CuAssertIntEquals(tc, 4, build(u, &bf.cons, 0, 20)); + CuAssertIntEquals(tc, 4, build(u, &bf.cons, 0, 20, 0)); CuAssertIntEquals(tc, 2, get_effect(u, ptype)); teardown_build(&bf); } @@ -410,7 +388,6 @@ CuSuite *get_build_suite(void) SUITE_ADD_TEST(suite, test_build_failure_low_skill); SUITE_ADD_TEST(suite, test_build_failure_missing_skill); SUITE_ADD_TEST(suite, test_build_requires_materials); - SUITE_ADD_TEST(suite, test_build_requires_building); SUITE_ADD_TEST(suite, test_build_failure_completed); SUITE_ADD_TEST(suite, test_build_with_ring); SUITE_ADD_TEST(suite, test_build_with_potion); diff --git a/src/kernel/building.c b/src/kernel/building.c index 6152622e8..bcc914cca 100644 --- a/src/kernel/building.c +++ b/src/kernel/building.c @@ -41,11 +41,12 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include -#include #include #include #include +#include +#include /* libc includes */ #include @@ -55,7 +56,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* attributes includes */ #include -#include typedef struct building_typelist { struct building_typelist *next; @@ -63,26 +63,28 @@ typedef struct building_typelist { } building_typelist; selist *buildingtypes = NULL; +static critbit_tree cb_bldgtypes; /* Returns a building type for the (internal) name */ static building_type *bt_find_i(const char *name) { - selist *ql; - int qi; + const char *match; + building_type *btype = NULL; - assert(name); - - for (qi = 0, ql = buildingtypes; ql; selist_advance(&ql, &qi, 1)) { - building_type *btype = (building_type *)selist_get(ql, qi); - if (strcmp(btype->_name, name) == 0) - return btype; + match = cb_find_str(&cb_bldgtypes, name); + if (match) { + cb_get_kv(match, &btype, sizeof(btype)); } - return NULL; + return btype; } const building_type *bt_find(const char *name) { - return bt_find_i(name); + building_type *btype = bt_find_i(name); + if (!btype) { + log_warning("bt_find: could not find building '%s'\n", name); + } + return btype; } static int bt_changes = 1; @@ -97,12 +99,15 @@ bool bt_changed(int *cache) return false; } -void bt_register(building_type * type) +static void bt_register(building_type * btype) { - if (type->init) { - type->init(type); - } - selist_push(&buildingtypes, (void *)type); + size_t len; + char data[64]; + + selist_push(&buildingtypes, (void *)btype); + len = cb_new_kv(btype->_name, strlen(btype->_name), &btype, sizeof(btype), data); + assert(len <= sizeof(data)); + cb_insert(&cb_bldgtypes, data, len); ++bt_changes; } @@ -115,6 +120,7 @@ static void free_buildingtype(void *ptr) { } void free_buildingtypes(void) { + cb_clear(&cb_bldgtypes); selist_foreach(buildingtypes, free_buildingtype); selist_free(buildingtypes); buildingtypes = 0; @@ -123,6 +129,7 @@ void free_buildingtypes(void) { building_type *bt_get_or_create(const char *name) { + assert(name && name[0]); if (name != NULL) { building_type *btype = bt_find_i(name); if (btype == NULL) { @@ -160,27 +167,48 @@ attrib_type at_building_generic_type = { ATF_UNIQUE }; +/* TECH DEBT: simplest thing that works for E3 dwarf/halfling faction rules */ +static int adjust_size(const building *b, int bsize) { + assert(b); + if (config_get_int("rules.dwarf_castles", 0) + && strcmp(b->type->_name, "castle") == 0) { + unit *u = building_owner(b); + if (u && u->faction->race == get_race(RC_HALFLING)) { + return bsize * 5 / 4; + } + } + return bsize; +} + /* Returns the (internal) name for a building of given size and type. Especially, returns the correct * name if it depends on the size (as for Eressea castles). */ const char *buildingtype(const building_type * btype, const building * b, int bsize) { - const char *s; + const construction *con; + assert(btype); - s = btype->_name; - if (btype->name) { - s = btype->name(btype, b, bsize); - } if (b && b->attribs) { if (is_building_type(btype, "generic")) { const attrib *a = a_find(b->attribs, &at_building_generic_type); if (a) { - s = (const char *)a->data.v; + return (const char *)a->data.v; } } } - return s; + if (btype->construction && btype->construction->name) { + if (b) { + bsize = adjust_size(b, bsize); + } + for (con = btype->construction; con; con = con->improvement) { + bsize -= con->maxsize; + if (!con->improvement || bsize <0) { + return con->name; + } + } + } + return btype->_name; } #define BMAXHASH 7919 @@ -222,80 +250,6 @@ building *findbuilding(int i) { return bfindhash(i); } - -/* ** old building types ** */ - -static int sm_smithy(const unit * u, const region * r, skill_t sk, int value) -{ /* skillmod */ - if (sk == SK_WEAPONSMITH || sk == SK_ARMORER) { - if (u->region == r) - return value + 1; - } - return value; -} - -static int mm_smithy(const unit * u, const resource_type * rtype, int value) -{ /* material-mod */ - if (rtype == get_resourcetype(R_IRON)) - return value * 2; - return value; -} - -static void init_smithy(struct building_type *bt) -{ - a_add(&bt->attribs, make_skillmod(NOSKILL, SMF_PRODUCTION, sm_smithy, 1.0, - 0)); - a_add(&bt->attribs, make_matmod(mm_smithy)); -} - -static const char *castle_name_i(const struct building_type *btype, - const struct building *b, int bsize, const char *fname[]) -{ - int i = bt_effsize(btype, b, bsize); - - return fname[i]; -} - -static const char *castle_name_2(const struct building_type *btype, - const struct building *b, int bsize) -{ - const char *fname[] = { - "site", - "fortification", - "tower", - "castle", - "fortress", - "citadel" - }; - return castle_name_i(btype, b, bsize, fname); -} - -static const char *castle_name(const struct building_type *btype, - const struct building *b, int bsize) -{ - const char *fname[] = { - "site", - "tradepost", - "fortification", - "tower", - "castle", - "fortress", - "citadel" - }; - return castle_name_i(btype, b, bsize, fname); -} - -static const char *fort_name(const struct building_type *btype, - const struct building *b, int bsize) -{ - const char *fname[] = { - "scaffolding", - "guardhouse", - "guardtower", - }; - return castle_name_i(btype, b, bsize, fname); -} - /* for finding out what was meant by a particular building string */ static local_names *bnames; @@ -511,24 +465,19 @@ int buildingeffsize(const building * b, int img) int bt_effsize(const building_type * btype, const building * b, int bsize) { - int i = bsize, n = 0; + int n = 0; const construction *cons = btype->construction; - /* TECH DEBT: simplest thing that works for E3 dwarf/halfling faction rules */ - if (b && config_get_int("rules.dwarf_castles", 0) - && strcmp(btype->_name, "castle") == 0) { - unit *u = building_owner(b); - if (u && u->faction->race == get_race(RC_HALFLING)) { - i = bsize * 10 / 8; - } + if (b) { + bsize = adjust_size(b, bsize); } - if (!cons || !cons->improvement) { + if (!cons) { return 0; } - while (cons && cons->maxsize != -1 && i >= cons->maxsize) { - i -= cons->maxsize; + while (cons && cons->maxsize != -1 && bsize >= cons->maxsize) { + bsize -= cons->maxsize; cons = cons->improvement; ++n; } @@ -577,10 +526,10 @@ static unit *building_owner_ex(const building * bld, const struct faction * last } if (!heir && config_token("rules.region_owner_pay_building", bld->type->_name)) { if (rule_region_owners()) { - u = building_owner(largestbuilding(bld->region, &cmp_taxes, false)); + u = building_owner(largestbuilding(bld->region, cmp_taxes, false)); } else { - u = building_owner(largestbuilding(bld->region, &cmp_wage, false)); + u = building_owner(largestbuilding(bld->region, cmp_wage, false)); } if (u) { heir = u; @@ -699,7 +648,7 @@ building *largestbuilding(const region * r, cmp_building_cb cmp_gt, { building *b, *best = NULL; - for (b = rbuildings(r); b; b = b->next) { + for (b = r->buildings; b; b = b->next) { if (cmp_gt(b, best) <= 0) continue; if (!imaginary) { @@ -727,7 +676,7 @@ static const int wagetable[7][4] = { static int default_wage(const region * r, const faction * f, const race * rc, int in_turn) { - building *b = largestbuilding(r, &cmp_wage, false); + building *b = largestbuilding(r, cmp_wage, false); int esize = 0; double wage; static int ct_cache; @@ -758,19 +707,12 @@ default_wage(const region * r, const faction * f, const race * rc, int in_turn) else { wage = wagetable[esize][2]; } - if (rule_blessed_harvest() == HARVEST_WORK) { + if (r->attribs && rule_blessed_harvest() == HARVEST_WORK) { /* E1 rules */ wage += curse_geteffect(get_curse(r->attribs, ct_find("blessedharvest"))); } } - /* Artsculpture: Income +5 */ - for (b = r->buildings; b; b = b->next) { - if (is_building_type(b->type, "artsculpture")) { - wage += 5; - } - } - if (r->attribs) { attrib *a; const struct curse_type *ctype; @@ -808,10 +750,18 @@ minimum_wage(const region * r, const faction * f, const race * rc, int in_turn) * die Bauern wenn f == NULL. */ int wage(const region * r, const faction * f, const race * rc, int in_turn) { - if (global.functions.wage) { - return global.functions.wage(r, f, rc, in_turn); + static int config; + static int rule_wage; + if (config_changed(&config)) { + rule_wage = config_get_int("rules.wage.function", 1); } - return default_wage(r, f, rc, in_turn); + if (rule_wage==0) { + return 0; + } + if (rule_wage==1) { + return default_wage(r, f, rc, in_turn); + } + return minimum_wage(r, f, rc, in_turn); } int cmp_wage(const struct building *b, const building * a) @@ -837,6 +787,12 @@ bool is_owner_building(const struct building * b) return false; } +int building_taxes(const building *b) { + assert(b); + return b->type->taxes; +} + + int cmp_taxes(const building * b, const building * a) { faction *f = region_get_owner(b->region); @@ -846,14 +802,12 @@ int cmp_taxes(const building * b, const building * a) return -1; } else if (a) { - int newsize = buildingeffsize(b, false); - double newtaxes = b->type->taxes(b, newsize); - int oldsize = buildingeffsize(a, false); - double oldtaxes = a->type->taxes(a, oldsize); + int newtaxes = building_taxes(b); + int oldtaxes = building_taxes(a); - if (newtaxes < oldtaxes) + if (newtaxes > oldtaxes) return -1; - else if (newtaxes > oldtaxes) + else if (newtaxes < oldtaxes) return 1; else if (b->size < a->size) return -1; @@ -862,8 +816,9 @@ int cmp_taxes(const building * b, const building * a) else { if (u && u->faction == f) { u = building_owner(a); - if (u && u->faction == f) - return -1; + if (u && u->faction == f) { + return 0; + } return 1; } } @@ -872,7 +827,7 @@ int cmp_taxes(const building * b, const building * a) return 1; } } - return -1; + return 0; } int cmp_current_owner(const building * b, const building * a) @@ -885,10 +840,8 @@ int cmp_current_owner(const building * b, const building * a) if (!u || u->faction != f) return -1; if (a) { - int newsize = buildingeffsize(b, false); - double newtaxes = b->type->taxes(b, newsize); - int oldsize = buildingeffsize(a, false); - double oldtaxes = a->type->taxes(a, oldsize); + int newtaxes = building_taxes(b); + int oldtaxes = building_taxes(a); if (newtaxes > oldtaxes) { return 1; @@ -896,23 +849,11 @@ int cmp_current_owner(const building * b, const building * a) if (newtaxes < oldtaxes) { return -1; } - if (newsize != oldsize) { - return newsize - oldsize; - } return (b->size - a->size); } else { return 1; } } - return -1; -} - -void register_buildings(void) -{ - register_function((pf_generic)minimum_wage, "minimum_wage"); - register_function((pf_generic)init_smithy, "init_smithy"); - register_function((pf_generic)castle_name, "castle_name"); - register_function((pf_generic)castle_name_2, "castle_name_2"); - register_function((pf_generic)fort_name, "fort_name"); + return 0; } diff --git a/src/kernel/building.h b/src/kernel/building.h index 9cb675fc1..04ad08262 100644 --- a/src/kernel/building.h +++ b/src/kernel/building.h @@ -63,16 +63,11 @@ extern "C" { variant magres; /* how well it resists against spells */ int magresbonus; /* bonus it gives the target against spells */ int fumblebonus; /* bonus that reduces fumbling */ + int taxes; /* receive $1 tax per `taxes` in region */ double auraregen; /* modifier for aura regeneration inside building */ struct maintenance *maintenance; /* array of requirements */ struct construction *construction; /* construction of 1 building-level */ - - const char *(*name) (const struct building_type *, - const struct building * b, int size); - void(*init) (struct building_type *); - void(*age) (struct building *); - double(*taxes) (const struct building *, int size); - struct attrib *attribs; + struct resource_mod *modifiers; /* modify production skills */ } building_type; extern struct selist *buildingtypes; @@ -85,8 +80,6 @@ extern "C" { bool bt_changed(int *cache); const building_type *bt_find(const char *name); void free_buildingtypes(void); - void register_buildings(void); - void bt_register(struct building_type *type); int bt_effsize(const struct building_type *btype, const struct building *b, int bsize); @@ -141,6 +134,7 @@ extern "C" { int cmp_taxes(const struct building *b, const struct building *bother); int cmp_current_owner(const struct building *b, const struct building *bother); + int building_taxes(const building *b); /* old functions, still in build.c: */ int buildingeffsize(const building * b, int imaginary); diff --git a/src/kernel/building.test.c b/src/kernel/building.test.c index 3f415a913..588ec2937 100644 --- a/src/kernel/building.test.c +++ b/src/kernel/building.test.c @@ -21,14 +21,13 @@ static void test_register_building(CuTest * tc) test_cleanup(); - btype = (building_type *)calloc(sizeof(building_type), 1); - btype->_name = strdup("herp"); CuAssertIntEquals(tc, true, bt_changed(&cache)); CuAssertIntEquals(tc, false, bt_changed(&cache)); - bt_register(btype); - CuAssertIntEquals(tc, true, bt_changed(&cache)); - CuAssertPtrNotNull(tc, bt_find("herp")); + btype = bt_get_or_create("herp"); + CuAssertIntEquals(tc, true, bt_changed(&cache)); + CuAssertPtrEquals(tc, btype, (void *)bt_find("herp")); + free_buildingtypes(); CuAssertIntEquals(tc, true, bt_changed(&cache)); test_cleanup(); @@ -295,11 +294,8 @@ static void test_btype_defaults(CuTest *tc) { CuAssertStrEquals(tc, "hodor", btype->_name); CuAssertPtrEquals(tc, 0, btype->maintenance); CuAssertPtrEquals(tc, 0, btype->construction); - CuAssertTrue(tc, !btype->name); - CuAssertTrue(tc, !btype->init); - CuAssertTrue(tc, !btype->age); - CuAssertTrue(tc, !btype->taxes); CuAssertDblEquals(tc, 1.0, btype->auraregen, 0.0); + CuAssertIntEquals(tc, 0, btype->taxes); CuAssertIntEquals(tc, -1, btype->maxsize); CuAssertIntEquals(tc, 1, btype->capacity); CuAssertIntEquals(tc, -1, btype->maxcapacity); @@ -428,10 +424,87 @@ static void test_cmp_castle_size(CuTest *tc) { u_set_building(u2, b2); b1->size = 5; b2->size = 10; - CuAssertTrue(tc, cmp_castle_size(b1, b2)<0); - CuAssertTrue(tc, cmp_castle_size(b2, b1)>0); - CuAssertTrue(tc, cmp_castle_size(b1, NULL)>0); - CuAssertTrue(tc, cmp_castle_size(NULL, b1)<0); + CuAssertTrue(tc, cmp_castle_size(b1, b2) < 0); + CuAssertTrue(tc, cmp_castle_size(b2, b1) > 0); + CuAssertTrue(tc, cmp_castle_size(b1, b1) == 0); + test_cleanup(); +} + +static void test_cmp_wage(CuTest *tc) { + region *r; + building *b1, *b2; + building_type *btype; + + test_setup(); + btype = test_create_buildingtype("castle"); + btype->taxes = 100; + r = test_create_region(0, 0, 0); + b1 = test_create_building(r, btype); + b2 = test_create_building(r, btype); + b1->size = 5; + b2->size = 10; + CuAssertPtrEquals(tc, b2, largestbuilding(r, cmp_wage, false)); + CuAssertTrue(tc, cmp_wage(b1, b2) < 0); + CuAssertTrue(tc, cmp_wage(b2, b1) > 0); + CuAssertTrue(tc, cmp_wage(b1, b1) == 0); + test_cleanup(); +} + +static void test_cmp_taxes(CuTest *tc) { + region *r; + building *b1, *b2; + building_type *btype; + unit *u1, *u2; + + test_setup(); + btype = test_create_buildingtype("castle"); + btype->taxes = 100; + r = test_create_region(0, 0, 0); + b1 = test_create_building(r, btype); + b2 = test_create_building(r, btype); + b1->size = 5; + b2->size = 10; + u1 = test_create_unit(test_create_faction(0), r); + u_set_building(u1, b1); + u2 = test_create_unit(test_create_faction(0), r); + u_set_building(u2, b2); + CuAssertPtrEquals(tc, b2, largestbuilding(r, cmp_taxes, false)); + CuAssertTrue(tc, cmp_taxes(b1, b2) < 0); + CuAssertTrue(tc, cmp_taxes(b2, b1) > 0); + CuAssertTrue(tc, cmp_taxes(b1, b1) == 0); + test_cleanup(); +} + +static void test_cmp_current_owner(CuTest *tc) { + region *r; + building *b1, *b2; + building_type *btype; + unit *u1, *u2; + + test_setup(); + config_set("rules.region_owners", "1"); + r = test_create_region(0, 0, 0); + btype = test_create_buildingtype("watch"); + btype->construction->maxsize = 1; + btype->taxes = 200; + b1 = test_create_building(r, btype); + btype = test_create_buildingtype("castle"); + btype->construction->maxsize = 1; + btype->taxes = 100; + b2 = test_create_building(r, btype); + b1->size = 1; + CuAssertIntEquals(tc, 1, buildingeffsize(b1, false)); + b2->size = 1; + CuAssertIntEquals(tc, 1, buildingeffsize(b2, false)); + u1 = test_create_unit(test_create_faction(0), r); + u_set_building(u1, b1); + u2 = test_create_unit(test_create_faction(0), r); + u_set_building(u2, b2); + region_set_owner(r, u1->faction, turn); + CuAssertPtrEquals(tc, b1, largestbuilding(r, cmp_current_owner, false)); + CuAssertTrue(tc, cmp_current_owner(b2, b1) < 0); + CuAssertTrue(tc, cmp_current_owner(b1, b2) > 0); + CuAssertTrue(tc, cmp_current_owner(b1, b1) == 0); test_cleanup(); } @@ -460,10 +533,52 @@ static void test_building_effsize(CuTest *tc) { test_cleanup(); } +static int cmp_size(const building *lhs, const building *rhs) { + assert(lhs); + return rhs ? lhs->size - rhs->size : 1; +} + +static void test_largestbuilding(CuTest *tc) { + region *r; + building *b1, *b2; + test_setup(); + r = test_create_region(0, 0, NULL); + CuAssertPtrEquals(tc, NULL, largestbuilding(r, cmp_size, false)); + b1 = test_create_building(r, NULL); + b2 = test_create_building(r, NULL); + b1->size = 1; + b2->size = 1; + CuAssertPtrEquals(tc, b1, largestbuilding(r, cmp_size, false)); + b1->size = 2; + CuAssertPtrEquals(tc, b1, largestbuilding(r, cmp_size, false)); + b2->size = 3; + CuAssertPtrEquals(tc, b2, largestbuilding(r, cmp_size, false)); + test_cleanup(); +} + +static void test_buildingtype(CuTest *tc) { + building_type *btype; + test_setup(); + btype = test_create_buildingtype("hodor"); + CuAssertPtrNotNull(tc, btype->construction); + CuAssertStrEquals(tc, "hodor", buildingtype(btype, NULL, 1)); + btype->construction->name = strdup("castle"); + CuAssertStrEquals(tc, "castle", buildingtype(btype, NULL, 1)); + btype = bt_get_or_create("portal"); + CuAssertPtrEquals(tc, NULL, btype->construction); + CuAssertStrEquals(tc, "portal", buildingtype(btype, NULL, 1)); + test_cleanup(); +} + CuSuite *get_building_suite(void) { CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_buildingtype); + SUITE_ADD_TEST(suite, test_largestbuilding); SUITE_ADD_TEST(suite, test_cmp_castle_size); + SUITE_ADD_TEST(suite, test_cmp_taxes); + SUITE_ADD_TEST(suite, test_cmp_wage); + SUITE_ADD_TEST(suite, test_cmp_current_owner); SUITE_ADD_TEST(suite, test_register_building); SUITE_ADD_TEST(suite, test_btype_defaults); SUITE_ADD_TEST(suite, test_building_set_owner); diff --git a/src/kernel/callbacks.c b/src/kernel/callbacks.c new file mode 100644 index 000000000..c1d52352a --- /dev/null +++ b/src/kernel/callbacks.c @@ -0,0 +1,5 @@ +#include +#include "callbacks.h" + +struct callback_struct callbacks = { 0 }; + diff --git a/src/attributes/matmod.h b/src/kernel/callbacks.h similarity index 61% rename from src/attributes/matmod.h rename to src/kernel/callbacks.h index 99171d338..dcdf6ac90 100644 --- a/src/attributes/matmod.h +++ b/src/kernel/callbacks.h @@ -16,22 +16,31 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. **/ -#ifndef H_ATTRIBUTE_MATMOD -#define H_ATTRIBUTE_MATMOD +#ifndef H_KRNL_CALLBACKS_H +#define H_KRNL_CALLBACKS_H #ifdef __cplusplus extern "C" { #endif - struct resource_type; + struct castorder; + struct order; struct unit; - typedef int(*mm_fun) (const struct unit * u, - const struct resource_type * rtype, int value); + struct region; + struct item_type; + struct resource_type; - extern struct attrib_type at_matmod; - extern struct attrib *make_matmod(mm_fun function); + struct callback_struct { + int (*cast_spell)(struct castorder *co, const char *fname); + int (*use_item)(struct unit *u, const struct item_type *itype, + int amount, struct order *ord); + void(*produce_resource)(struct region *, const struct resource_type *, int); + int(*limit_resource)(const struct region *, const struct resource_type *); + }; + extern struct callback_struct callbacks; #ifdef __cplusplus } #endif -#endif +#endif /* H_KRNL_CALLBACKS_H */ + diff --git a/src/kernel/config.c b/src/kernel/config.c index bf61c8f02..37765914b 100644 --- a/src/kernel/config.c +++ b/src/kernel/config.c @@ -93,12 +93,10 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include -struct settings global = { - "Eressea", /* gamename */ -}; +struct settings global; bool lomem = false; -int turn = -1; +int turn = 0; const char *parameters[MAXPARAMS] = { "LOCALE", @@ -367,13 +365,26 @@ void init_options_translation(const struct locale * lang) { } } -void init_locale(struct locale *lang) +void init_races(struct locale *lang) { - variant var; - int i; const struct race *rc; void **tokens; + tokens = get_translations(lang, UT_RACES); + for (rc = races; rc; rc = rc->next) { + const char *name; + variant var; + var.v = (void *)rc; + name = locale_string(lang, rc_name_s(rc, NAME_PLURAL), false); + if (name) addtoken((struct tnode **)tokens, name, var); + name = locale_string(lang, rc_name_s(rc, NAME_SINGULAR), false); + if (name) addtoken((struct tnode **)tokens, name, var); + } +} + +static void init_magic(struct locale *lang) +{ + void **tokens; tokens = get_translations(lang, UT_MAGIC); if (tokens) { const char *str = config_get("rules.magic.playerschools"); @@ -385,7 +396,9 @@ void init_locale(struct locale *lang) sstr = strdup(str); tok = strtok(sstr, " "); while (tok) { + variant var; const char *name; + int i; for (i = 0; i != MAXMAGIETYP; ++i) { if (strcmp(tok, magic_school[i]) == 0) break; } @@ -402,21 +415,14 @@ void init_locale(struct locale *lang) } free(sstr); } - +} +void init_locale(struct locale *lang) +{ + init_magic(lang); init_directions(lang); init_keywords(lang); init_skills(lang); - - tokens = get_translations(lang, UT_RACES); - for (rc = races; rc; rc = rc->next) { - const char *name; - var.v = (void *)rc; - name = locale_string(lang, rc_name_s(rc, NAME_PLURAL), false); - if (name) addtoken((struct tnode **)tokens, name, var); - name = locale_string(lang, rc_name_s(rc, NAME_SINGULAR), false); - if (name) addtoken((struct tnode **)tokens, name, var); - } - + init_races(lang); init_parameters(lang); init_options_translation(lang); @@ -733,7 +739,7 @@ bool config_changed(int *cache_key) { } #define MAXKEYS 16 -void config_set_from(const dictionary *d) +void config_set_from(const dictionary *d, const char *valid_keys[]) { int s, nsec = iniparser_getnsec(d); for (s=0;s!=nsec;++s) { @@ -742,6 +748,7 @@ void config_set_from(const dictionary *d) int k, nkeys = iniparser_getsecnkeys(d, sec); const char *keys[MAXKEYS]; size_t slen = strlen(sec); + assert(nkeys <= MAXKEYS); assert(slenunits; u; u = u->nextF) { const race *rc = u_race(u); - int x = (flags&COUNT_UNITS) ? 1 : u->number; + int x = u->number; if (f->race != rc) { if (!playerrace(rc)) { if (flags&COUNT_MONSTERS) { @@ -732,16 +732,6 @@ int count_faction(const faction * f, int flags) return n; } -int count_units(const faction * f) -{ - return count_faction(f, COUNT_ALL | COUNT_UNITS); -} - -int count_all(const faction * f) -{ - return count_faction(f, COUNT_ALL); -} - int count_migrants(const faction * f) { return count_faction(f, COUNT_MIGRANTS); @@ -752,7 +742,7 @@ int count_maxmigrants(const faction * f) int formula = rc_migrants_formula(f->race); if (formula == MIGRANTS_LOG10) { - int nsize = count_all(f); + int nsize = f->num_people; if (nsize > 0) { int x = (int)(log10(nsize / 50.0) * 20); if (x < 0) x = 0; @@ -762,22 +752,6 @@ int count_maxmigrants(const faction * f) return 0; } -static void init_maxmagicians(struct attrib *a) -{ - a->data.i = MAXMAGICIANS; -} - -attrib_type at_maxmagicians = { - "maxmagicians", - init_maxmagicians, - NULL, - NULL, - a_writeint, - a_readint, - NULL, - ATF_UNIQUE -}; - int max_magicians(const faction * f) { static int rule, config, rc_cache; @@ -785,15 +759,9 @@ int max_magicians(const faction * f) int m; if (config_changed(&config)) { - rule = config_get_int("rules.maxskills.magic", MAXMAGICIANS); + rule = config_get_int("rules.maxskills.magic", 3); } m = rule; - if (f->attribs) { - attrib *a = a_find(f->attribs, &at_maxmagicians); - if (a) { - m = a->data.i; - } - } if (rc_changed(&rc_cache)) { rc_elf = get_race(RC_ELF); } diff --git a/src/kernel/faction.h b/src/kernel/faction.h index 0a0ac39d6..e0cd893a1 100644 --- a/src/kernel/faction.h +++ b/src/kernel/faction.h @@ -35,8 +35,6 @@ extern "C" { struct gamedata; struct selist; - extern struct attrib_type at_maxmagicians; - /* faction flags */ #define FFL_NEWID (1<<0) /* Die Partei hat bereits einmal ihre no gewechselt */ #define FFL_ISNEW (1<<1) @@ -44,8 +42,8 @@ extern "C" { #define FFL_QUIT (1<<3) #define FFL_CURSED (1<<4) /* you're going to have a bad time */ #define FFL_DEFENDER (1<<10) -#define FFL_SELECT (1<<18) /* ehemals f->dh, u->dh, r->dh, etc... */ #define FFL_NOAID (1<<21) /* Hilfsflag Kampf */ +#define FFL_SELECT (1<<22) /* ehemals f->dh, u->dh, r->dh, etc... */ #define FFL_MARK (1<<23) /* f�r markierende algorithmen, die das * hinterher auch wieder l�schen m�ssen! * (FFL_SELECT muss man vorher initialisieren, @@ -77,13 +75,10 @@ extern "C" { magic_t magiegebiet; int newbies; int num_people; /* Anzahl Personen ohne Monster */ - int num_total; /* Anzahl Personen mit Monstern */ + int num_units; int options; - int no_units; struct ally *allies; /* alliedgroup and others should check sf.faction.alive before using a faction from f.allies */ struct group *groups; /* alliedgroup and others should check sf.faction.alive before using a faction from f.groups */ - int nregions; - int money; score_t score; struct alliance *alliance; int alliance_joindate; /* the turn on which the faction joined its current alliance (or left the last one) */ @@ -166,14 +161,10 @@ extern "C" { #define COUNT_MONSTERS 0x01 #define COUNT_MIGRANTS 0x02 #define COUNT_DEFAULT 0x04 -#define COUNT_ALL 0x07 -#define COUNT_UNITS 0x10 int count_faction(const struct faction * f, int flags); int count_migrants(const struct faction * f); int count_maxmigrants(const struct faction * f); - int count_all(const struct faction * f); - int count_units(const struct faction * f); int max_magicians(const struct faction * f); struct faction *getfaction(void); diff --git a/src/kernel/item.c b/src/kernel/item.c index 605511005..04274d633 100644 --- a/src/kernel/item.c +++ b/src/kernel/item.c @@ -34,23 +34,18 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "terrain.h" #include "unit.h" -/* triggers includes */ -#include -#include - /* util includes */ #include #include -#include -#include #include #include #include #include #include -#include #include +#include +#include #include /* libc includes */ @@ -568,48 +563,6 @@ item *i_new(const item_type * itype, int size) return i; } -#include "region.h" - -static int -give_horses(unit * s, unit * d, const item_type * itype, int n, -struct order *ord) -{ - if (d == NULL) { - int use = use_pooled(s, item2resource(itype), GET_SLACK, n); - region *r = s->region; - if (use < n) { - use += - use_pooled(s, item2resource(itype), GET_RESERVE | GET_POOLED_SLACK, - n - use); - } - if (r->land) { - rsethorses(r, rhorses(r) + use); - } - return 0; - } - return -1; /* use the mechanism */ -} - -static int -give_money(unit * s, unit * d, const item_type * itype, int n, -struct order *ord) -{ - if (d == NULL) { - int use = use_pooled(s, item2resource(itype), GET_SLACK, n); - region *r = s->region; - if (use < n) { - use += - use_pooled(s, item2resource(itype), GET_RESERVE | GET_POOLED_SLACK, - n - use); - } - if (r->land) { - rsetmoney(r, rmoney(r) + use); - } - return 0; - } - return -1; /* use the mechanism */ -} - const potion_type *oldpotiontype[MAX_POTIONS + 1]; /*** alte items ***/ @@ -668,27 +621,6 @@ int set_item(unit * u, const item_type *itype, int value) return value; } -static int -use_birthdayamulet(unit * u, const struct item_type *itype, int amount, -struct order *ord) -{ - direction_t d; - message *msg = msg_message("meow", ""); - - UNUSED_ARG(ord); - UNUSED_ARG(amount); - UNUSED_ARG(itype); - - add_message(&u->region->msgs, msg); - for (d = 0; d < MAXDIRECTIONS; d++) { - region *tr = rconnect(u->region, d); - if (tr) - add_message(&tr->msgs, msg); - } - msg_release(msg); - return 0; -} - /* t_item::flags */ #define FL_ITEM_CURSED (1<<0) #define FL_ITEM_NOTLOST (1<<1) @@ -696,34 +628,6 @@ struct order *ord) #define FL_ITEM_ANIMAL (1<<3) /* ist ein Tier */ #define FL_ITEM_MOUNT ((1<<4) | FL_ITEM_ANIMAL) /* ist ein Reittier */ -/* ------------------------------------------------------------- */ -/* Kann auch von Nichtmagier benutzt werden, modifiziert Taktik fuer diese - * Runde um -1 - 4 Punkte. */ -static int -use_tacticcrystal(unit * u, const struct item_type *itype, int amount, -struct order *ord) -{ - int i; - for (i = 0; i != amount; ++i) { - int duration = 1; /* wirkt nur eine Runde */ - curse *c; - float effect; - float power = 5; /* Widerstand gegen Antimagiesprueche, ist in diesem - Fall egal, da der curse fuer den Kampf gelten soll, - der vor den Antimagiezaubern passiert */ - - effect = (float)(rng_int() % 6 - 1); - c = create_curse(u, &u->attribs, ct_find("skillmod"), power, - duration, effect, u->number); - c->data.i = SK_TACTICS; - UNUSED_ARG(ord); - } - use_pooled(u, itype->rtype, GET_DEFAULT, amount); - ADDMSG(&u->faction->msgs, msg_message("use_tacticcrystal", - "unit region", u, u->region)); - return 0; -} - typedef struct t_item { const char *name; /* [0]: Einzahl fuer eigene; [1]: Mehrzahl fuer eigene; @@ -759,17 +663,6 @@ mod_dwarves_only(const unit * u, const region * r, skill_t sk, int value) return -118; } -static int heal(unit * user, int effect) -{ - int req = unit_max_hp(user) * user->number - user->hp; - if (req > 0) { - req = MIN(req, effect); - effect -= req; - user->hp += req; - } - return effect; -} - void register_item_give(int(*foo) (struct unit *, struct unit *, const struct item_type *, int, struct order *), const char *name) @@ -784,173 +677,6 @@ struct order *), const char *name) register_function((pf_generic)foo, name); } -void -register_item_useonother(int(*foo) (struct unit *, int, -const struct item_type *, int, struct order *), const char *name) -{ - register_function((pf_generic)foo, name); -} - -static int -use_healingpotion(struct unit *user, const struct item_type *itype, int amount, -struct order *ord) -{ - int effect = amount * 400; - unit *u = user->region->units; - effect = heal(user, effect); - while (effect > 0 && u != NULL) { - if (u->faction == user->faction) { - effect = heal(u, effect); - } - u = u->next; - } - use_pooled(user, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK, - amount); - usetpotionuse(user, itype->rtype->ptype); - - ADDMSG(&user->faction->msgs, msg_message("usepotion", - "unit potion", user, itype->rtype)); - return 0; -} - -static int -use_warmthpotion(struct unit *u, const struct item_type *itype, int amount, -struct order *ord) -{ - if (u->faction->race == get_race(RC_INSECT)) { - fset(u, UFL_WARMTH); - } - else { - /* nur fuer insekten: */ - cmistake(u, ord, 163, MSG_EVENT); - return ECUSTOM; - } - use_pooled(u, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK, - amount); - usetpotionuse(u, itype->rtype->ptype); - - ADDMSG(&u->faction->msgs, msg_message("usepotion", - "unit potion", u, itype->rtype)); - return 0; -} - -static int -use_foolpotion(struct unit *u, int targetno, const struct item_type *itype, -int amount, struct order *ord) -{ - unit *target = findunit(targetno); - if (target == NULL || u->region != target->region) { - ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found", - "")); - return ECUSTOM; - } - if (effskill(u, SK_STEALTH, 0) <= effskill(target, SK_PERCEPTION, 0)) { - cmistake(u, ord, 64, MSG_EVENT); - return ECUSTOM; - } - ADDMSG(&u->faction->msgs, msg_message("givedumb", - "unit recipient amount", u, target, amount)); - - change_effect(target, itype->rtype->ptype, amount); - use_pooled(u, itype->rtype, GET_DEFAULT, amount); - return 0; -} - -static int -use_bloodpotion(struct unit *u, const struct item_type *itype, int amount, -struct order *ord) -{ - if (u->number == 0 || u_race(u) == get_race(RC_DAEMON)) { - change_effect(u, itype->rtype->ptype, 100 * amount); - } - else { - const race *irace = u_irace(u); - if (irace == u_race(u)) { - const race *rcfailure = rc_find("smurf"); - if (!rcfailure) { - rcfailure = rc_find("toad"); - } - if (rcfailure) { - trigger *trestore = trigger_changerace(u, u_race(u), irace); - if (trestore) { - int duration = 2 + rng_int() % 8; - - add_trigger(&u->attribs, "timer", trigger_timeout(duration, - trestore)); - u->irace = NULL; - u_setrace(u, rcfailure); - } - } - } - } - use_pooled(u, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK, - amount); - usetpotionuse(u, itype->rtype->ptype); - - ADDMSG(&u->faction->msgs, msg_message("usepotion", - "unit potion", u, itype->rtype)); - return 0; -} - -#include -static int -use_mistletoe(struct unit *user, const struct item_type *itype, int amount, -struct order *ord) -{ - int mtoes = - get_pooled(user, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK, - user->number); - - if (user->number > mtoes) { - ADDMSG(&user->faction->msgs, msg_message("use_singleperson", - "unit item region command", user, itype->rtype, user->region, ord)); - return -1; - } - use_pooled(user, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK, - user->number); - a_add(&user->attribs, make_fleechance((float)1.0)); - ADDMSG(&user->faction->msgs, - msg_message("use_item", "unit item", user, itype->rtype)); - - return 0; -} - -static int -use_magicboost(struct unit *user, const struct item_type *itype, int amount, -struct order *ord) -{ - int mtoes = - get_pooled(user, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK, - user->number); - faction *f = user->faction; - if (user->number > mtoes) { - ADDMSG(&user->faction->msgs, msg_message("use_singleperson", - "unit item region command", user, itype->rtype, user->region, ord)); - return -1; - } - if (!is_mage(user) || key_get(f->attribs, atoi36("mbst"))) { - cmistake(user, user->thisorder, 214, MSG_EVENT); - return -1; - } - use_pooled(user, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK, - user->number); - - key_set(&f->attribs, atoi36("mbst"), turn); - set_level(user, SK_MAGIC, 3); - - ADDMSG(&user->faction->msgs, msg_message("use_item", - "unit item", user, itype->rtype)); - - return 0; -} - -static int -use_snowball(struct unit *user, const struct item_type *itype, int amount, -struct order *ord) -{ - return 0; -} - static void init_oldpotions(void) { const char *potionnames[MAX_POTIONS] = { @@ -982,7 +708,6 @@ void init_resources(void) rtype->uchange = res_changeitem; rtype->itype = it_get_or_create(rtype); rtype->itype->weight = 1; - rtype->itype->give = give_money; rtype = rt_get_or_create(resourcenames[R_HORSE]); rtype->flags |= RTF_ITEM | RTF_LIMITED; @@ -1186,25 +911,27 @@ void read_items(struct storage *store, item ** ilist) { for (;;) { char ibuf[32]; - const item_type *itype; int i; READ_STR(store, ibuf, sizeof(ibuf)); if (!strcmp("end", ibuf)) { break; } - itype = it_find(ibuf); READ_INT(store, &i); - if (i <= 0) { - log_error("data contains an entry with %d %s", i, ibuf); - } - else { - if (itype && itype->rtype) { - i_change(ilist, itype, i); + if (ilist) { + const item_type *itype; + itype = it_find(ibuf); + if (i <= 0) { + log_error("data contains an entry with %d %s", i, ibuf); } else { - log_error("data contains unknown item type %s.", ibuf); + if (itype && itype->rtype) { + i_change(ilist, itype, i); + } + else { + log_error("data contains unknown item type %s.", ibuf); + } + assert(itype && itype->rtype); } - assert(itype && itype->rtype); } } } @@ -1242,15 +969,12 @@ void free_rtype(resource_type *rtype) { if (rtype->wtype) { free_wtype(rtype->wtype); } - if (rtype->atype) { - free(rtype->atype); - } if (rtype->itype) { free_itype(rtype->itype); } - if (rtype->raw) { - free(rtype->raw); - } + free(rtype->atype); + free(rtype->modifiers); + free(rtype->raw); free(rtype->_name); free(rtype); } @@ -1296,18 +1020,4 @@ void register_resources(void) register_function((pf_generic)res_changepermaura, "changepermaura"); register_function((pf_generic)res_changehp, "changehp"); register_function((pf_generic)res_changeaura, "changeaura"); - - register_item_use(use_potion, "usepotion"); - register_item_use(use_potion_delayed, "usepotion_delayed"); - register_item_use(use_tacticcrystal, "use_tacticcrystal"); - register_item_use(use_birthdayamulet, "use_birthdayamulet"); - register_item_use(use_warmthpotion, "usewarmthpotion"); - register_item_use(use_bloodpotion, "usebloodpotion"); - register_item_use(use_healingpotion, "usehealingpotion"); - register_item_useonother(use_foolpotion, "usefoolpotion"); - register_item_use(use_mistletoe, "usemistletoe"); - register_item_use(use_magicboost, "usemagicboost"); - register_item_use(use_snowball, "usesnowball"); - - register_item_give(give_horses, "givehorses"); } diff --git a/src/kernel/item.h b/src/kernel/item.h index bb114c8ac..51ebcd2a1 100644 --- a/src/kernel/item.h +++ b/src/kernel/item.h @@ -39,7 +39,7 @@ extern "C" { struct storage; struct gamedata; struct rawmaterial_type; - struct resource_limit; + struct resource_mod; typedef struct item { struct item *next; @@ -68,8 +68,6 @@ extern "C" { typedef int(*rtype_uchange) (struct unit * user, const struct resource_type * rtype, int delta); - typedef int(*rtype_uget) (const struct unit * user, - const struct resource_type * rtype); typedef char *(*rtype_name) (const struct resource_type * rtype, int flags); typedef struct resource_type { /* --- constants --- */ @@ -77,12 +75,10 @@ extern "C" { unsigned int flags; /* --- functions --- */ rtype_uchange uchange; - rtype_uget uget; rtype_name name; struct rawmaterial_type *raw; - struct resource_limit *limit; + struct resource_mod *modifiers; /* --- pointers --- */ - struct attrib *attribs; struct item_type *itype; struct potion_type *ptype; struct luxury_type *ltype; @@ -94,11 +90,6 @@ extern "C" { const resource_type *findresourcetype(const char *name, const struct locale *lang); - /* resource-limits for regions */ -#define RMF_SKILL 0x01 /* int, bonus on resource production skill */ -#define RMF_SAVEMATERIAL 0x02 /* fraction (sa[0]/sa[1]), multiplier on resource usage */ -#define RMF_REQUIREDBUILDING 0x04 /* building, required to build */ - /* bitfield values for item_type::flags */ #define ITF_NONE 0x0000 #define ITF_HERB 0x0001 /* this item is a herb */ @@ -107,6 +98,7 @@ extern "C" { #define ITF_BIG 0x0008 /* big item, e.g. does not fit in a bag of holding */ #define ITF_ANIMAL 0x0010 /* an animal */ #define ITF_VEHICLE 0x0020 /* a vehicle, drawn by two animals */ +#define ITF_CANUSE 0x0040 /* can be used with use_item_fun callout */ /* error codes for item_type::use */ #define ECUSTOM -1 @@ -120,17 +112,10 @@ extern "C" { unsigned int flags; int weight; int capacity; + int mask_allow; + int mask_deny; struct construction *construction; char *_appearance[2]; /* wie es f�r andere aussieht */ - /* --- functions --- */ - bool(*canuse) (const struct unit * user, - const struct item_type * itype); - int(*use) (struct unit * user, const struct item_type * itype, int amount, - struct order * ord); - int(*useonother) (struct unit * user, int targetno, - const struct item_type * itype, int amount, struct order * ord); - int(*give) (struct unit * src, struct unit * dest, - const struct item_type * itm, int number, struct order * ord); int score; } item_type; @@ -321,8 +306,6 @@ extern "C" { const struct item_type *, int, struct order *), const char *name); void register_item_use(int(*foo) (struct unit *, const struct item_type *, int, struct order *), const char *name); - void register_item_useonother(int(*foo) (struct unit *, int, - const struct item_type *, int, struct order *), const char *name); void free_resources(void); diff --git a/src/kernel/item.test.c b/src/kernel/item.test.c index 6e3615797..c1c64bf62 100644 --- a/src/kernel/item.test.c +++ b/src/kernel/item.test.c @@ -162,7 +162,6 @@ static void test_core_resources(CuTest *tc) { CuAssertPtrNotNull(tc, rtype = rt_find("money")); CuAssertPtrNotNull(tc, rtype->itype); CuAssertPtrNotNull(tc, rtype->uchange); - CuAssertPtrNotNull(tc, rtype->itype->give); CuAssertPtrNotNull(tc, rtype = rt_find("peasant")); CuAssertPtrEquals(tc, 0, rtype->itype); CuAssertPtrNotNull(tc, rtype = rt_find("permaura")); diff --git a/src/kernel/jsonconf.c b/src/kernel/jsonconf.c index 0f5642c42..609e73aac 100644 --- a/src/kernel/jsonconf.c +++ b/src/kernel/jsonconf.c @@ -33,6 +33,8 @@ without prior permission by the authors of Eressea. /* game modules */ #include "prefix.h" +#include "move.h" +#include "calendar.h" /* util includes */ #include @@ -337,11 +339,6 @@ static void json_building(cJSON *json, building_type *bt) { } break; case cJSON_String: - if (strcmp(child->string, "name") == 0) { - bt->name = (const char *(*)(const struct building_type *, - const struct building *, int))get_function(child->valuestring); - break; - } log_error("building %s contains unknown attribute %s", json->string, child->string); break; default: @@ -588,18 +585,12 @@ static void json_spells(cJSON *json) { for (child = json->child; child; child = child->next) { if (child->type == cJSON_Object) { spell *sp; - cJSON * item = cJSON_GetObjectItem(child, "index"); - sp = create_spell(child->string, item ? item->valueint : 0); + cJSON * item; + sp = create_spell(child->string); for (item = child->child; item; item = item->next) { if (strcmp(item->string, "index") == 0) { continue; } - else if (strcmp(item->string, "cast") == 0) { - sp->cast = (spell_f)get_function(item->valuestring); - } - else if (strcmp(item->string, "fumble") == 0) { - sp->fumble = (fumble_f)get_function(item->valuestring); - } else if (strcmp(item->string, "syntax") == 0) { sp->syntax = strdup(item->valuestring); } @@ -690,6 +681,74 @@ static void json_direction(cJSON *json, struct locale *lang) { } } +static void json_calendar(cJSON *json) { + cJSON *child; + if (json->type != cJSON_Object) { + log_error("calendar is not an object: %d", json->type); + return; + } + for (child = json->child; child; child = child->next) { + if (strcmp(child->string, "start") == 0) { + config_set_int("game.start", child->valueint); + } + else if (strcmp(child->string, "weeks") == 0) { + cJSON *entry; + int i; + if (child->type != cJSON_Array) { + log_error("calendar.weeks is not an array: %d", json->type); + return; + } + weeks_per_month = cJSON_GetArraySize(child); + free(weeknames); + weeknames = malloc(sizeof(char *) * weeks_per_month); + for (i = 0, entry = child->child; entry; entry = entry->next, ++i) { + if (entry->type == cJSON_String) { + weeknames[i] = strdup(entry->valuestring); + } + else { + log_error("calendar.weeks[%d] is not a string: %d", i, json->type); + free(weeknames); + weeknames = NULL; + return; + } + } + assert(i == weeks_per_month); + free(weeknames2); + weeknames2 = malloc(sizeof(char *) * weeks_per_month); + for (i = 0; i != weeks_per_month; ++i) { + weeknames2[i] = malloc(strlen(weeknames[i]) + 3); + sprintf(weeknames2[i], "%s_d", weeknames[i]); + } + } + else if (strcmp(child->string, "months") == 0) { + cJSON *jmonth; + int i; + if (child->type != cJSON_Array) { + log_error("calendar.seasons is not an array: %d", json->type); + return; + } + free(month_season); + month_season = NULL; + free(storms); + months_per_year = cJSON_GetArraySize(child); + storms = malloc(sizeof(int) * months_per_year); + month_season = malloc(sizeof(int) * months_per_year); + for (i = 0, jmonth = child->child; jmonth; jmonth = jmonth->next, ++i) { + if (jmonth->type == cJSON_Object) { + storms[i] = cJSON_GetObjectItem(jmonth, "storm")->valueint; + month_season[i] = cJSON_GetObjectItem(jmonth, "season")->valueint; + } + else { + log_error("calendar.months[%d] is not an object: %d", i, json->type); + free(storms); + storms = NULL; + return; + } + } + } + } +} + static void json_directions(cJSON *json) { cJSON *child; if (json->type != cJSON_Object) { @@ -896,6 +955,9 @@ void json_config(cJSON *json) { else if (strcmp(child->string, "strings") == 0) { json_strings(child); } + else if (strcmp(child->string, "calendar") == 0) { + json_calendar(child); + } else if (strcmp(child->string, "directions") == 0) { json_directions(child); } diff --git a/src/kernel/jsonconf.test.c b/src/kernel/jsonconf.test.c index 456a27d22..2f54eeb8d 100644 --- a/src/kernel/jsonconf.test.c +++ b/src/kernel/jsonconf.test.c @@ -13,6 +13,8 @@ #include "order.h" #include "terrain.h" +#include "move.h" +#include "calendar.h" #include "prefix.h" #include "util/language.h" @@ -75,7 +77,7 @@ static void test_settings(CuTest * tc) "\"float\" : 1.5 }}"; cJSON *json = cJSON_Parse(data); - test_cleanup(); + test_setup(); config_set("game.id", "42"); /* should not be replaced */ config_set("game.name", "Eressea"); /* should not be replaced */ json_config(json); @@ -99,7 +101,7 @@ static void test_prefixes(CuTest * tc) "]}"; cJSON *json = cJSON_Parse(data); - test_cleanup(); + test_setup(); json_config(json); CuAssertPtrNotNull(tc, race_prefixes); CuAssertStrEquals(tc, "snow", race_prefixes[0]); @@ -119,7 +121,7 @@ static void test_disable(CuTest * tc) "]}"; cJSON *json = cJSON_Parse(data); - test_cleanup(); + test_setup(); CuAssertTrue(tc, skill_enabled(SK_ALCHEMY)); CuAssertTrue(tc, !keyword_disabled(K_BANNER)); CuAssertTrue(tc, !keyword_disabled(K_PAY)); @@ -135,6 +137,31 @@ static void test_disable(CuTest * tc) test_cleanup(); } +static void test_calendar(CuTest * tc) +{ + const char * data = "{\"calendar\": { " + "\"weeks\" : [ \"one\", \"two\", \"three\" ]," + "\"months\" : [" + "{ \"storm\" : 99, \"season\" : 1 }," + "{ \"storm\" : 22, \"season\" : 2 }" + "]" + "}}"; + cJSON *json = cJSON_Parse(data); + + test_setup(); + json_config(json); + CuAssertPtrNotNull(tc, storms); + CuAssertIntEquals(tc, 2, months_per_year); + CuAssertIntEquals(tc, 3, weeks_per_month); + CuAssertIntEquals(tc, 99, storms[0]); + CuAssertIntEquals(tc, 22, storms[1]); + CuAssertPtrNotNull(tc, month_season); + CuAssertIntEquals(tc, 1, month_season[0]); + CuAssertIntEquals(tc, 2, month_season[1]); + cJSON_Delete(json); + test_cleanup(); +} + static void test_races(CuTest * tc) { const char * data = "{\"races\": { \"orc\" : { " @@ -155,7 +182,7 @@ static void test_races(CuTest * tc) cJSON *json = cJSON_Parse(data); const struct race *rc; - test_cleanup(); + test_setup(); CuAssertPtrNotNull(tc, json); CuAssertPtrEquals(tc, 0, races); @@ -189,7 +216,7 @@ static void test_findrace(CuTest *tc) { const race *rc; CuAssertPtrNotNull(tc, json); - test_cleanup(); + test_setup(); lang = get_or_create_locale("de"); CuAssertPtrEquals(tc, 0, (void *)findrace("Zwerg", lang)); @@ -211,7 +238,7 @@ static void test_items(CuTest * tc) cJSON *json = cJSON_Parse(data); const item_type * itype; - test_cleanup(); + test_setup(); CuAssertPtrNotNull(tc, json); CuAssertPtrEquals(tc, 0, it_find("axe")); @@ -249,7 +276,7 @@ static void test_ships(CuTest * tc) const ship_type *st; const terrain_type *ter; - test_cleanup(); + test_setup(); CuAssertPtrNotNull(tc, json); CuAssertPtrEquals(tc, 0, shiptypes); @@ -286,7 +313,7 @@ static void test_castles(CuTest *tc) { cJSON *json = cJSON_Parse(data); const building_type *bt; - test_cleanup(); + test_setup(); CuAssertPtrNotNull(tc, json); CuAssertPtrEquals(tc, 0, buildingtypes); @@ -311,7 +338,7 @@ static void test_spells(CuTest * tc) cJSON *json = cJSON_Parse(data); const spell *sp; - test_cleanup(); + test_setup(); CuAssertPtrNotNull(tc, json); CuAssertPtrEquals(tc, 0, find_spell("fireball")); @@ -350,7 +377,7 @@ static void test_buildings(CuTest * tc) cJSON *json = cJSON_Parse(building_data); const building_type *bt; - test_cleanup(); + test_setup(); CuAssertPtrNotNull(tc, json); CuAssertPtrEquals(tc, 0, buildingtypes); @@ -400,7 +427,7 @@ static void test_buildings_default(CuTest * tc) const building_type *bt; building_type clone; - test_cleanup(); + test_setup(); bt = bt_get_or_create("house"); clone = *bt; @@ -425,7 +452,7 @@ static void test_ships_default(CuTest * tc) const ship_type *st; ship_type clone; - test_cleanup(); + test_setup(); st = st_get_or_create("hodor"); clone = *st; @@ -446,7 +473,7 @@ static void test_configs(CuTest * tc) FILE *F; cJSON *json = cJSON_Parse(data); - test_cleanup(); + test_setup(); F = fopen("test.json", "w"); fwrite(building_data, 1, strlen(building_data), F); @@ -475,7 +502,7 @@ static void test_terrains(CuTest * tc) cJSON *json = cJSON_Parse(data); - test_cleanup(); + test_setup(); CuAssertPtrNotNull(tc, json); CuAssertPtrEquals(tc, 0, (void *)get_terrain("plain")); @@ -511,7 +538,7 @@ static void test_directions(CuTest * tc) cJSON *json = cJSON_Parse(data); - test_cleanup(); + test_setup(); lang = get_or_create_locale("de"); CuAssertPtrNotNull(tc, json); CuAssertIntEquals(tc, NODIRECTION, get_direction("ost", lang)); @@ -533,7 +560,7 @@ static void test_skills(CuTest * tc) cJSON *json = cJSON_Parse(data); - test_cleanup(); + test_setup(); lang = get_or_create_locale("de"); CuAssertPtrNotNull(tc, json); CuAssertIntEquals(tc, NOSKILL, get_skill("potato", lang)); @@ -558,7 +585,7 @@ static void test_keywords(CuTest * tc) cJSON *json = cJSON_Parse(data); - test_cleanup(); + test_setup(); lang = get_or_create_locale("de"); CuAssertPtrNotNull(tc, json); CuAssertIntEquals(tc, NOKEYWORD, get_keyword("potato", lang)); @@ -583,7 +610,7 @@ static void test_strings(CuTest * tc) cJSON *json = cJSON_Parse(data); CuAssertPtrNotNull(tc, json); - test_cleanup(); + test_setup(); lang = get_or_create_locale("de"); CuAssertPtrNotNull(tc, lang); CuAssertPtrEquals(tc, NULL, (void *)LOC(lang, "move")); @@ -640,6 +667,7 @@ CuSuite *get_jsonconf_suite(void) SUITE_ADD_TEST(suite, test_prefixes); SUITE_ADD_TEST(suite, test_disable); SUITE_ADD_TEST(suite, test_infinitive_from_config); + SUITE_ADD_TEST(suite, test_calendar); return suite; } diff --git a/src/kernel/messages.c b/src/kernel/messages.c index f74e9c0d7..d3cd4f99b 100644 --- a/src/kernel/messages.c +++ b/src/kernel/messages.c @@ -78,16 +78,14 @@ variant v) struct message *msg_feedback(const struct unit *u, struct order *ord, const char *name, const char *sig, ...) { - va_list marker; const message_type *mtype = mt_find(name); - char paramname[64]; - const char *ic = sig; variant args[16]; variant var; memset(args, 0, sizeof(args)); - if (ord == NULL) - ord = u->thisorder; + if (ord && ord->_noerror) { + return NULL; + } if (!mtype) { log_warning("trying to create message of unknown type \"%s\"\n", name); @@ -106,41 +104,46 @@ struct message *msg_feedback(const struct unit *u, struct order *ord, var.v = (void *)ord; arg_set(args, mtype, "command", var); - va_start(marker, sig); - while (*ic && !isalnum(*ic)) - ic++; - while (*ic) { - char *oc = paramname; - int i; + if (sig) { + const char *ic = sig; + va_list marker; - while (isalnum(*ic)) - *oc++ = *ic++; - *oc = '\0'; - - for (i = 0; i != mtype->nparameters; ++i) { - if (!strcmp(paramname, mtype->pnames[i])) - break; - } - if (i != mtype->nparameters) { - if (mtype->types[i]->vtype == VAR_VOIDPTR) { - args[i].v = va_arg(marker, void *); - } - else if (mtype->types[i]->vtype == VAR_INT) { - args[i].i = va_arg(marker, int); - } - else { - assert(!"unknown variant type"); - } - } - else { - log_error("invalid parameter %s for message type %s\n", paramname, mtype->name); - assert(!"program aborted."); - } + va_start(marker, sig); while (*ic && !isalnum(*ic)) ic++; - } - va_end(marker); + while (*ic) { + char paramname[64]; + char *oc = paramname; + int i; + while (isalnum(*ic)) + *oc++ = *ic++; + *oc = '\0'; + + for (i = 0; i != mtype->nparameters; ++i) { + if (!strcmp(paramname, mtype->pnames[i])) + break; + } + if (i != mtype->nparameters) { + if (mtype->types[i]->vtype == VAR_VOIDPTR) { + args[i].v = va_arg(marker, void *); + } + else if (mtype->types[i]->vtype == VAR_INT) { + args[i].i = va_arg(marker, int); + } + else { + assert(!"unknown variant type"); + } + } + else { + log_error("invalid parameter %s for message type %s\n", paramname, mtype->name); + assert(!"program aborted."); + } + while (*ic && !isalnum(*ic)) + ic++; + } + va_end(marker); + } return msg_create(mtype, args); } @@ -248,8 +251,9 @@ void addmessage(region * r, faction * f, const char *s, msg_t mtype, int level) message * msg_error(const unit * u, struct order *ord, int mno) { static char msgname[20]; - if (fval(u->faction, FFL_NPC)) - return 0; + if (fval(u->faction, FFL_NPC)) { + return NULL; + } sprintf(msgname, "error%d", mno); return msg_feedback(u, ord, msgname, ""); } @@ -259,7 +263,9 @@ message * cmistake(const unit * u, struct order *ord, int mno, int mtype) message * result; UNUSED_ARG(mtype); result = msg_error(u, ord, mno); - ADDMSG(&u->faction->msgs, result); + if (result) { + ADDMSG(&u->faction->msgs, result); + } return result; } @@ -267,7 +273,9 @@ void syntax_error(const struct unit *u, struct order *ord) { message * result; result = msg_error(u, ord, 10); - ADDMSG(&u->faction->msgs, result); + if (result) { + ADDMSG(&u->faction->msgs, result); + } } void free_messagelist(mlist *msgs) diff --git a/src/kernel/messages.test.c b/src/kernel/messages.test.c index 7b0f2dd5e..cd301756d 100644 --- a/src/kernel/messages.test.c +++ b/src/kernel/messages.test.c @@ -1,6 +1,9 @@ #include #include "messages.h" +#include "unit.h" +#include "order.h" + #include #include @@ -68,11 +71,27 @@ static void test_merge_split(CuTest *tc) { test_cleanup(); } +static void test_noerror(CuTest *tc) { + unit *u; + struct locale *lang; + + test_setup(); + lang = test_create_locale(); + u = test_create_unit(test_create_faction(NULL), test_create_region(0, 0, NULL)); + u->thisorder = parse_order("!@move", lang); + CuAssertTrue(tc, u->thisorder->_persistent); + CuAssertTrue(tc, u->thisorder->_noerror); + CuAssertPtrEquals(tc, NULL, msg_error(u, u->thisorder, 100)); + CuAssertPtrEquals(tc, NULL, msg_feedback(u, u->thisorder, "error_unit_not_found", NULL)); + test_cleanup(); +} + CuSuite *get_messages_suite(void) { CuSuite *suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_missing_message); SUITE_ADD_TEST(suite, test_merge_split); SUITE_ADD_TEST(suite, test_message); + SUITE_ADD_TEST(suite, test_noerror); return suite; } diff --git a/src/kernel/order.c b/src/kernel/order.c index 776190b67..6fbb53040 100644 --- a/src/kernel/order.c +++ b/src/kernel/order.c @@ -44,13 +44,8 @@ static struct locale_data *locale_array[MAXLOCALES]; typedef struct order_data { const char *_str; -# ifdef LOMEM - int _refcount:20; - int _lindex:4; -# else int _refcount; int _lindex; -# endif keyword_t _keyword; } order_data; @@ -99,6 +94,15 @@ char* get_command(const order *ord, char *sbuffer, size_t size) { keyword_t kwd = ORD_KEYWORD(ord); int bytes; + if (ord->_noerror) { + if (size > 0) { + *bufp++ = '!'; + --size; + } + else { + WARN_STATIC_BUFFER(); + } + } if (ord->_persistent) { if (size > 0) { *bufp++ = '@'; @@ -159,6 +163,7 @@ order *copy_order(const order * src) order *ord = (order *)malloc(sizeof(order)); ord->next = NULL; ord->_persistent = src->_persistent; + ord->_noerror = src->_noerror; ord->data = src->data; ++ord->data->_refcount; return ord; @@ -280,7 +285,7 @@ void close_orders(void) { } static order *create_order_i(keyword_t kwd, const char *sptr, bool persistent, - const struct locale *lang) + bool noerror, const struct locale *lang) { order *ord = NULL; int lindex; @@ -313,6 +318,7 @@ static order *create_order_i(keyword_t kwd, const char *sptr, bool persistent, ord = (order *)malloc(sizeof(order)); ord->_persistent = persistent; + ord->_noerror = noerror; ord->next = NULL; ord->data = create_data(kwd, sptr, lindex); @@ -373,7 +379,7 @@ order *create_order(keyword_t kwd, const struct locale * lang, else { zBuffer[0] = 0; } - return create_order_i(kwd, zBuffer, false, lang); + return create_order_i(kwd, zBuffer, false, false, lang); } order *parse_order(const char *s, const struct locale * lang) @@ -386,11 +392,12 @@ order *parse_order(const char *s, const struct locale * lang) if (*s != 0) { keyword_t kwd; const char *sptr; - bool persistent = false; + bool persistent = false, noerror = false; const char * p; - while (*s == '@') { - persistent = true; + while (*s == '!' || *s=='@') { + if (*s=='!') noerror = true; + else if (*s == '@') persistent = true; ++s; } sptr = s; @@ -407,7 +414,7 @@ order *parse_order(const char *s, const struct locale * lang) if (kwd != NOKEYWORD) { while (isspace(*(unsigned char *)sptr)) ++sptr; s = sptr; - return create_order_i(kwd, s, persistent, lang); + return create_order_i(kwd, s, persistent, noerror, lang); } } return NULL; diff --git a/src/kernel/order.h b/src/kernel/order.h index 7f4b00a5e..bf4d02982 100644 --- a/src/kernel/order.h +++ b/src/kernel/order.h @@ -37,6 +37,7 @@ extern "C" { /* do not access this data: */ struct order_data *data; bool _persistent; + bool _noerror; } order; /* constructor */ diff --git a/src/kernel/order.test.c b/src/kernel/order.test.c index f5eb51c6f..876d75b1e 100644 --- a/src/kernel/order.test.c +++ b/src/kernel/order.test.c @@ -14,14 +14,13 @@ static void test_create_order(CuTest *tc) { order *ord; struct locale * lang; - test_cleanup(); - lang = get_or_create_locale("en"); + test_setup(); + lang = test_create_locale(); - locale_setstring(lang, "keyword::move", "MOVE"); ord = create_order(K_MOVE, lang, "NORTH"); CuAssertPtrNotNull(tc, ord); CuAssertIntEquals(tc, K_MOVE, getkeyword(ord)); - CuAssertStrEquals(tc, "MOVE NORTH", get_command(ord, cmd, sizeof(cmd))); + CuAssertStrEquals(tc, "move NORTH", get_command(ord, cmd, sizeof(cmd))); CuAssertIntEquals(tc, K_MOVE, init_order(ord)); CuAssertStrEquals(tc, "NORTH", getstrtoken()); @@ -34,19 +33,44 @@ static void test_parse_order(CuTest *tc) { order *ord; struct locale * lang; - test_cleanup(); - lang = get_or_create_locale("en"); + test_setup(); + lang = test_create_locale(); - locale_setstring(lang, "keyword::move", "MOVE"); - init_keyword(lang, K_MOVE, "MOVE"); ord = parse_order("MOVE NORTH", lang); CuAssertPtrNotNull(tc, ord); + CuAssertTrue(tc, !ord->_noerror); + CuAssertTrue(tc, !ord->_persistent); CuAssertIntEquals(tc, K_MOVE, getkeyword(ord)); - CuAssertStrEquals(tc, "MOVE NORTH", get_command(ord, cmd, sizeof(cmd))); + CuAssertStrEquals(tc, "move NORTH", get_command(ord, cmd, sizeof(cmd))); CuAssertIntEquals(tc, K_MOVE, init_order(ord)); CuAssertStrEquals(tc, "NORTH", getstrtoken()); free_order(ord); + + ord = parse_order("!MOVE NORTH", lang); + CuAssertPtrNotNull(tc, ord); + CuAssertTrue(tc, ord->_noerror); + CuAssertTrue(tc, !ord->_persistent); + free_order(ord); + + ord = parse_order("@MOVE NORTH", lang); + CuAssertPtrNotNull(tc, ord); + CuAssertTrue(tc, !ord->_noerror); + CuAssertTrue(tc, ord->_persistent); + free_order(ord); + + ord = parse_order("@!MOVE NORTH", lang); + CuAssertPtrNotNull(tc, ord); + CuAssertTrue(tc, ord->_noerror); + CuAssertTrue(tc, ord->_persistent); + free_order(ord); + + ord = parse_order("!@MOVE NORTH", lang); + CuAssertPtrNotNull(tc, ord); + CuAssertTrue(tc, ord->_noerror); + CuAssertTrue(tc, ord->_persistent); + free_order(ord); + test_cleanup(); } @@ -81,7 +105,7 @@ static void test_parse_make_temp(CuTest *tc) { lang = get_or_create_locale("en"); locale_setstring(lang, keyword(K_MAKE), "MAKE"); locale_setstring(lang, keyword(K_MAKETEMP), "MAKETEMP"); - locale_setstring(lang, "TEMP", "TEMP"); + locale_setstring(lang, parameters[P_TEMP], "TEMP"); init_locale(lang); ord = parse_order("M T herp", lang); @@ -156,9 +180,9 @@ static void test_replace_order(CuTest *tc) { struct locale * lang; test_cleanup(); - lang = get_or_create_locale("en"); - orig = create_order(K_MAKE, lang, 0); - repl = create_order(K_ALLY, lang, 0); + lang = test_create_locale(); + orig = create_order(K_MAKE, lang, NULL); + repl = create_order(K_ALLY, lang, NULL); replace_order(&orders, orig, repl); CuAssertPtrEquals(tc, 0, orders); orders = orig; @@ -171,6 +195,25 @@ static void test_replace_order(CuTest *tc) { test_cleanup(); } +static void test_get_command(CuTest *tc) { + struct locale * lang; + order *ord; + char buf[64]; + + test_setup(); + lang = test_create_locale(); + ord = create_order(K_MAKE, lang, "iron"); + CuAssertStrEquals(tc, "make iron", get_command(ord, buf, sizeof(buf))); + ord->_noerror = true; + CuAssertStrEquals(tc, "!make iron", get_command(ord, buf, sizeof(buf))); + ord->_persistent = true; + CuAssertStrEquals(tc, "!@make iron", get_command(ord, buf, sizeof(buf))); + ord->_noerror = false; + CuAssertStrEquals(tc, "@make iron", get_command(ord, buf, sizeof(buf))); + free_order(ord); + test_cleanup(); +} + CuSuite *get_order_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -183,5 +226,6 @@ CuSuite *get_order_suite(void) SUITE_ADD_TEST(suite, test_replace_order); SUITE_ADD_TEST(suite, test_skip_token); SUITE_ADD_TEST(suite, test_getstrtoken); + SUITE_ADD_TEST(suite, test_get_command); return suite; } diff --git a/src/kernel/pool.c b/src/kernel/pool.c index 702f86e0f..8dfd589c3 100644 --- a/src/kernel/pool.c +++ b/src/kernel/pool.c @@ -40,11 +40,11 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. int get_resource(const unit * u, const resource_type * rtype) { assert(rtype); - if (rtype->uget) { - /* this resource is probably special */ - int i = rtype->uget(u, rtype); - if (i >= 0) - return i; + if (rtype == get_resourcetype(R_PEASANT)) { + return u->region->land ? u->region->land->peasants : 0; + } + else if (rtype == rt_find("hp")) { + return u->hp; } else if (rtype->uchange) { /* this resource is probably special */ @@ -176,7 +176,7 @@ int count) } if (rtype->flags & RTF_POOLED && mode & ~(GET_SLACK | GET_RESERVE)) { for (v = r->units; v && use < count; v = v->next) - if (u != v && (v->items || rtype->uget)) { + if (u != v) { int mask; if ((u_race(v)->ec_flags & ECF_KEEP_ITEM)) @@ -234,8 +234,6 @@ use_pooled(unit * u, const resource_type * rtype, unsigned int mode, int count) int mask; if ((u_race(v)->ec_flags & ECF_KEEP_ITEM)) continue; - if (v->items == NULL && rtype->uget == NULL) - continue; if (v->faction == f) { mask = (mode >> 3) & (GET_SLACK | GET_RESERVE); diff --git a/src/kernel/race.c b/src/kernel/race.c index 077e03b28..a2c37d87d 100644 --- a/src/kernel/race.c +++ b/src/kernel/race.c @@ -54,6 +54,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* libc includes */ #include #include +#include #include #include #include @@ -64,7 +65,7 @@ race *races; int num_races = 0; static int rc_changes = 1; -static const char *racenames[MAXRACES] = { +const char *racenames[MAXRACES] = { "dwarf", "elf", NULL, "goblin", "human", "troll", "demon", "insect", "halfling", "cat", "aquarian", "orc", "snotling", "undead", NULL, "youngdragon", "dragon", "wyrm", "ent", "catdragon", "dracoid", @@ -94,6 +95,7 @@ enum { RCO_TRADEHERB }; + static void rc_setoption(race *rc, int k, const char *value) { unsigned char key = (unsigned char)k; int i; @@ -166,7 +168,8 @@ const struct race *findrace(const char *s, const struct locale *lang) const struct race *get_race(race_t rt) { const char * name; - assert(rt>=0 && rt < MAXRACES); + assert(rt >= 0); + assert(rt < MAXRACES); name = racenames[rt]; if (!name) { return NULL; @@ -323,6 +326,17 @@ bool rc_changed(int *cache) { return false; } +bool rc_can_use(const struct race *rc, const struct item_type *itype) +{ + if (itype->mask_allow) { + return (itype->mask_allow & rc->mask_item) != 0; + } + if (itype->mask_deny) { + return (itype->mask_deny & rc->mask_item) == 0; + } + return true; +} + race *rc_create(const char *zName) { race *rc; @@ -471,11 +485,9 @@ void rc_set_param(struct race *rc, const char *key, const char *value) { } } -const char* rc_name(const race * rc, name_t n, char *name, size_t size) { +const char* rc_key(const char *rcname, name_t n, char *name, size_t size) +{ const char * postfix = 0; - if (!rc) { - return NULL; - } switch (n) { case NAME_SINGULAR: postfix = ""; break; case NAME_PLURAL: postfix = "_p"; break; @@ -484,12 +496,20 @@ const char* rc_name(const race * rc, name_t n, char *name, size_t size) { default: assert(!"invalid name_t enum in rc_name_s"); } if (postfix) { - snprintf(name, size, "race::%s%s", rc->_name, postfix); + snprintf(name, size, "race::%s%s", rcname, postfix); return name; } return NULL; } +const char* rc_name(const race * rc, name_t n, char *name, size_t size) +{ + if (!rc) { + return NULL; + } + return rc_key(rc->_name, n, name, size); +} + const char *rc_name_s(const race * rc, name_t n) { static char name[64]; /* FIXME: static return value */ diff --git a/src/kernel/race.h b/src/kernel/race.h index f14b8e54f..5c320ea05 100644 --- a/src/kernel/race.h +++ b/src/kernel/race.h @@ -48,8 +48,7 @@ extern "C" { struct spellref; struct locale; struct rcoption; - - extern int num_races; + struct item_type; typedef enum { RC_DWARF, /* 0 - Zwerg */ @@ -102,6 +101,9 @@ extern "C" { NORACE = -1 } race_t; + extern int num_races; + extern const char *racenames[MAXRACES]; + typedef struct att { int type; union { @@ -140,6 +142,7 @@ extern "C" { int flags; int battle_flags; int ec_flags; + int mask_item; struct att attack[RACE_ATTACKS]; signed char bonus[MAXSKILLS]; @@ -174,8 +177,11 @@ extern "C" { const race *rc_find(const char *); void free_races(void); + bool rc_can_use(const struct race *rc, const struct item_type *itype); + typedef enum name_t { NAME_SINGULAR, NAME_PLURAL, NAME_DEFINITIVE, NAME_CATEGORY } name_t; const char * rc_name_s(const race *rc, name_t n); + const char * rc_key(const char *rcname, name_t n, char *name, size_t size); const char * rc_name(const race *rc, name_t n, char *name, size_t size); void rc_set_param(struct race *rc, const char *key, const char *value); diff --git a/src/kernel/race.test.c b/src/kernel/race.test.c index 07ecbdd45..2db76a6fd 100644 --- a/src/kernel/race.test.c +++ b/src/kernel/race.test.c @@ -1,10 +1,13 @@ #include #include #include "race.h" -#include +#include "item.h" + #include +#include #include +#include #include static void test_rc_name(CuTest *tc) { @@ -100,6 +103,54 @@ static void test_rc_set_param(CuTest *tc) { test_cleanup(); } +static void test_rc_can_use(CuTest *tc) { + race *rc; + item_type *itype; + + test_setup(); + rc = test_create_race("goblin"); + itype = test_create_itemtype("plate"); + CuAssertTrue(tc, rc_can_use(rc, itype)); + + /* default case. all items and races in E2 */ + itype->mask_deny = 0; + rc->mask_item = 0; + CuAssertTrue(tc, rc_can_use(rc, itype)); + + /* some race is forbidden from using this item. */ + itype->mask_deny = 1; + + /* we are not that race. */ + rc->mask_item = 2; + CuAssertTrue(tc, rc_can_use(rc, itype)); + + /* we are that race */ + rc->mask_item = 1; + CuAssertTrue(tc, ! rc_can_use(rc, itype)); + + /* we are not a special race at all */ + rc->mask_item = 0; + CuAssertTrue(tc, rc_can_use(rc, itype)); + + /* only one race is allowed to use this item */ + itype->mask_deny = 0; + itype->mask_allow = 1; + + /* we are not that race */ + rc->mask_item = 2; + CuAssertTrue(tc, ! rc_can_use(rc, itype)); + + /* we are that race */ + rc->mask_item = 1; + CuAssertTrue(tc, rc_can_use(rc, itype)); + + /* we are not special */ + rc->mask_item = 0; + CuAssertTrue(tc, ! rc_can_use(rc, itype)); + + test_cleanup(); +} + CuSuite *get_race_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -109,6 +160,7 @@ CuSuite *get_race_suite(void) SUITE_ADD_TEST(suite, test_rc_defaults); SUITE_ADD_TEST(suite, test_rc_find); SUITE_ADD_TEST(suite, test_rc_set_param); + SUITE_ADD_TEST(suite, test_rc_can_use); return suite; } diff --git a/src/kernel/region.c b/src/kernel/region.c index 5223d8bf8..5cb86ff47 100644 --- a/src/kernel/region.c +++ b/src/kernel/region.c @@ -38,6 +38,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "unit.h" /* util includes */ +#include #include #include #include @@ -697,6 +698,7 @@ void r_setdemand(region * r, const luxury_type * ltype, int value) d = *dp; if (!d) { d = *dp = malloc(sizeof(struct demand)); + assert_alloc(d); d->next = NULL; d->type = ltype; } @@ -768,6 +770,7 @@ region *new_region(int x, int y, struct plane *pl, int uid) return r; } r = calloc(1, sizeof(region)); + assert_alloc(r); r->x = x; r->y = y; r->uid = uid; @@ -1077,8 +1080,7 @@ void terraform_region(region * r, const terrain_type * terrain) if (!fval(terrain, LAND_REGION)) { region_setinfo(r, NULL); - if (r->land != NULL) { - i_freeall(&r->land->items); + if (r->land) { free_land(r->land); r->land = NULL; } @@ -1098,7 +1100,6 @@ void terraform_region(region * r, const terrain_type * terrain) for (d = 0; d != MAXDIRECTIONS; ++d) { rsetroad(r, d, 0); } - i_freeall(&r->land->items); } else { static struct surround { @@ -1173,11 +1174,6 @@ void terraform_region(region * r, const terrain_type * terrain) if (fval(terrain, LAND_REGION)) { const item_type *itype = NULL; - char equip_hash[64]; - - /* TODO: put the equipment in struct terrain, faster */ - sprintf(equip_hash, "terrain_%s", terrain->_name); - equip_items(&r->land->items, get_equipment(equip_hash)); if (r->terrain->herbs) { int len = 0; @@ -1227,7 +1223,7 @@ void terraform_region(region * r, const terrain_type * terrain) if (!fval(r, RF_CHAOTIC)) { int peasants; - peasants = (region_maxworkers(r) * (20 + dice_rand("6d10"))) / 100; + peasants = (region_maxworkers(r) * (20 + dice(6, 10))) / 100; rsetpeasants(r, MAX(100, peasants)); rsetmoney(r, rpeasants(r) * ((wage(r, NULL, NULL, INT_MAX) + 1) + rng_int() % 5)); @@ -1395,8 +1391,8 @@ faction *update_owners(region * r) faction *f = NULL; assert(rule_region_owners()); if (r->land) { - building *bowner = largestbuilding(r, &cmp_current_owner, false); - building *blargest = largestbuilding(r, &cmp_taxes, false); + building *bowner = largestbuilding(r, cmp_current_owner, false); + building *blargest = largestbuilding(r, cmp_taxes, false); if (blargest) { if (!bowner || bowner->size < blargest->size) { /* region owners update? */ @@ -1502,6 +1498,7 @@ int owner_change(const region * r) bool is_mourning(const region * r, int in_turn) { int change = owner_change(r); - return (change == in_turn - 1 && r->land->ownership->last_owner && r->land->ownership->owner - && r->land->ownership->last_owner != r->land->ownership->owner); + return (change == in_turn - 1 && r->land && + r->land->ownership->last_owner && r->land->ownership->owner && + r->land->ownership->last_owner != r->land->ownership->owner); } diff --git a/src/kernel/region.h b/src/kernel/region.h index 9fa1a2daa..e3204d129 100644 --- a/src/kernel/region.h +++ b/src/kernel/region.h @@ -66,7 +66,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. struct faction; struct gamedata; -#define MORALE_TAX_FACTOR 0.005 /* 0.5% tax per point of morale */ +#define MORALE_TAX_FACTOR 200 /* 0.5% tax per point of morale, 1 silver per 200 */ #define MORALE_MAX 10 /* Maximum morale allowed */ #define MORALE_DEFAULT 1 /* Morale of peasants when they are conquered for the first time */ #define MORALE_TAKEOVER 0 /* Morale of peasants after they lose their lord */ @@ -105,7 +105,6 @@ extern "C" { int peasants; int newpeasants; int money; - struct item *items; /* items that can be claimed */ struct region_owner *ownership; } land_region; diff --git a/src/kernel/resources.c b/src/kernel/resources.c index 8945e34c2..842cb83ba 100644 --- a/src/kernel/resources.c +++ b/src/kernel/resources.c @@ -11,10 +11,11 @@ */ #include -#include #include "resources.h" /* kernel includes */ +#include +#include #include "build.h" #include "item.h" #include "region.h" @@ -193,16 +194,6 @@ struct rawmaterial *rm_get(region * r, const struct resource_type *rtype) return rm; } -struct rawmaterial_type *rmt_find(const char *str) -{ - resource_type *rtype = rt_find(str); - if (!rtype && strncmp(str, "rm_", 3) == 0) { - rtype = rt_find(str+3); - } - assert(rtype); - return rtype ? rtype->raw : NULL; -} - struct rawmaterial_type *rmt_get(const struct resource_type *rtype) { return rtype->raw; @@ -222,3 +213,20 @@ struct rawmaterial_type *rmt_create(struct resource_type *rtype) rmtype->visible = visible_default; return rmtype; } + +int limit_resource(const struct region *r, const resource_type *rtype) +{ + assert(!rtype->raw); + if (callbacks.limit_resource) { + return callbacks.limit_resource(r, rtype); + } + return -1; +} + +void produce_resource(struct region *r, const struct resource_type *rtype, int amount) +{ + assert(!rtype->raw); + if (callbacks.produce_resource) { + callbacks.produce_resource(r, rtype, amount); + } +} diff --git a/src/kernel/resources.h b/src/kernel/resources.h index 04e122f76..2daa44dd8 100644 --- a/src/kernel/resources.h +++ b/src/kernel/resources.h @@ -15,8 +15,11 @@ extern "C" { #endif +#include + struct building_type; struct race; + struct region; enum { RM_USED = 1 << 0, /* resource has been used */ @@ -25,42 +28,31 @@ extern "C" { typedef struct rawmaterial { const struct resource_type *rtype; -#ifdef LOMEM - int amount:16; - int level:8; - int flags:8; - int base:8; - int divisor:8; - int startlevel:8; -#else int amount; int level; int flags; int base; int divisor; int startlevel; -#endif struct rawmaterial *next; } rawmaterial; - typedef int(*rlimit_limit) (const struct region * r, - const struct resource_type * rtype); - typedef void(*rlimit_produce) (struct region * r, - const struct resource_type * rtype, int n); + /* resource-limits for regions */ + typedef enum resource_modifier_type { + RMT_END, /* array terminator */ + RMT_PROD_SKILL, /* bonus on resource production skill */ + RMT_PROD_SAVE, /* fractional multiplier when produced */ + RMT_PROD_REQUIRE, /* building or race is required to produce this item */ + RMT_USE_SAVE, /* fractional multiplier when used to manufacture something */ + } resource_modifier_type; typedef struct resource_mod { + resource_modifier_type type; variant value; const struct building_type *btype; const struct race *race; - unsigned int flags; } resource_mod; - typedef struct resource_limit { - rlimit_limit limit; - rlimit_produce produce; - resource_mod *modifiers; - } resource_limit; - typedef struct rawmaterial_type { const struct resource_type *rtype; @@ -76,13 +68,15 @@ extern "C" { void terraform_resources(struct region *r); struct rawmaterial *rm_get(struct region *, const struct resource_type *); - struct rawmaterial_type *rmt_find(const char *str); struct rawmaterial_type *rmt_get(const struct resource_type *); struct rawmaterial *add_resource(struct region *r, int level, int base, int divisor, const struct resource_type *rtype); struct rawmaterial_type *rmt_create(struct resource_type *rtype); + int limit_resource(const struct region *r, const struct resource_type *rtype); + void produce_resource(struct region *r, const struct resource_type *rtype, int amount); + #ifdef __cplusplus } #endif diff --git a/src/kernel/rules.c b/src/kernel/rules.c new file mode 100644 index 000000000..c7499fe0c --- /dev/null +++ b/src/kernel/rules.c @@ -0,0 +1,12 @@ +#include +#include "rules.h" + +int write_rules(const char *filename) { + return -1; +} + +int read_rules(const char *filename) +{ + return -1; +} + diff --git a/src/kernel/rules.h b/src/kernel/rules.h new file mode 100644 index 000000000..98f0bf240 --- /dev/null +++ b/src/kernel/rules.h @@ -0,0 +1,5 @@ +#pragma once + +int read_rules(const char *filename); +int write_rules(const char * filename); + diff --git a/src/kernel/save.c b/src/kernel/save.c index c9bdb2f2e..39f2a129f 100644 --- a/src/kernel/save.c +++ b/src/kernel/save.c @@ -52,6 +52,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include /* util includes */ +#include #include #include #include @@ -243,7 +244,7 @@ static faction *factionorders(void) /* TODO: +1 ist ein Workaround, weil cturn erst in process_orders * incrementiert wird. */ - f->lastorders = global.data_turn + 1; + f->lastorders = turn + 1; } else { @@ -646,6 +647,7 @@ unit *read_unit(struct gamedata *data) } else { u = calloc(sizeof(unit), 1); + assert_alloc(u); u->no = n; uhash(u); } @@ -655,10 +657,7 @@ unit *read_unit(struct gamedata *data) if (f != u->faction) { u_setfaction(u, f); } - if (u->faction) { - ++u->faction->no_units; - } - else { + if (!u->faction) { log_error("unit %s has faction == NULL", itoa36(u->no)); return 0; } @@ -1022,7 +1021,9 @@ static region *readregion(struct gamedata *data, int x, int y) if (!r->land->demands) { fix_demand(r); } - read_items(data->store, &r->land->items); + if (data->version < NOLANDITEM_VERSION) { + read_items(data->store, NULL); + } if (data->version >= REGIONOWNER_VERSION) { READ_INT(data->store, &n); region_set_morale(r, MAX(0, (short)n), -1); @@ -1096,7 +1097,6 @@ void writeregion(struct gamedata *data, const region * r) WRITE_INT(data->store, demand->value); } WRITE_TOK(data->store, "end"); - write_items(data->store, r->land->items); WRITE_SECTION(data->store); #if RELEASE_VERSION>=REGIONOWNER_VERSION WRITE_INT(data->store, region_get_morale(r)); @@ -1122,9 +1122,7 @@ static ally **addally(const faction * f, ally ** sfp, int aid, int state) ally *sf; state &= ~HELP_OBSERVE; -#ifndef REGIONOWNERS state &= ~HELP_TRAVEL; -#endif state &= HelpMask(); if (state == 0) @@ -1637,7 +1635,6 @@ int read_game(gamedata *data) { } read_attribs(data, &global.attribs, NULL); READ_INT(store, &turn); - global.data_turn = turn; log_debug(" - reading turn %d", turn); rng_init(turn); READ_INT(store, NULL); /* max_unique_id = ignore */ diff --git a/src/kernel/ship.c b/src/kernel/ship.c index b9ac0b170..220c16619 100644 --- a/src/kernel/ship.c +++ b/src/kernel/ship.c @@ -37,13 +37,15 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include -#include #include #include #include +#include +#include /* libc includes */ #include @@ -51,7 +53,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include -selist *shiptypes = NULL; +selist *shiptypes = NULL; /* do not use this list for searching*/ +static critbit_tree cb_shiptypes; /* use this trie instead */ static local_names *snames; @@ -60,9 +63,7 @@ const ship_type *findshiptype(const char *name, const struct locale *lang) local_names *sn = snames; variant var; - while (sn) { - if (sn->lang == lang) - break; + while (sn && sn->lang != lang) { sn = sn->next; } if (!sn) { @@ -89,29 +90,43 @@ const ship_type *findshiptype(const char *name, const struct locale *lang) static ship_type *st_find_i(const char *name) { - selist *ql; - int qi; + const char *match; + ship_type *st = NULL; - for (qi = 0, ql = shiptypes; ql; selist_advance(&ql, &qi, 1)) { - ship_type *stype = (ship_type *)selist_get(ql, qi); - if (strcmp(stype->_name, name) == 0) { - return stype; - } + match = cb_find_str(&cb_shiptypes, name); + if (match) { + cb_get_kv(match, &st, sizeof(st)); } - return NULL; + return st; } const ship_type *st_find(const char *name) { - return st_find_i(name); + ship_type *st = st_find_i(name); + if (!st) { + log_warning("st_find: could not find ship '%s'\n", name); + } + return st; +} + +static void st_register(ship_type *stype) { + size_t len; + char data[64]; + + selist_push(&shiptypes, (void *)stype); + + len = cb_new_kv(stype->_name, strlen(stype->_name), &stype, sizeof(stype), data); + assert(len <= sizeof(data)); + cb_insert(&cb_shiptypes, data, len); } ship_type *st_get_or_create(const char * name) { ship_type * st = st_find_i(name); + assert(!snames); if (!st) { st = (ship_type *)calloc(sizeof(ship_type), 1); st->_name = strdup(name); st->storm = 1.0; - selist_push(&shiptypes, (void *)st); + st_register(st); } return st; } @@ -234,8 +249,9 @@ void remove_ship(ship ** slist, ship * sh) void free_ship(ship * s) { - while (s->attribs) + while (s->attribs) { a_remove(&s->attribs, s->attribs); + } free(s->name); free(s->display); free(s); @@ -250,6 +266,7 @@ static void free_shiptype(void *ptr) { } void free_shiptypes(void) { + cb_clear(&cb_shiptypes); selist_foreach(shiptypes, free_shiptype); selist_free(shiptypes); shiptypes = 0; diff --git a/src/kernel/skills.c b/src/kernel/skills.c index 488800115..b951b77ba 100644 --- a/src/kernel/skills.c +++ b/src/kernel/skills.c @@ -62,8 +62,7 @@ attrib_type at_skillmod = { ATF_PRESERVE }; -attrib *make_skillmod(skill_t sk, unsigned int flags, skillmod_fun special, - double multiplier, int bonus) +attrib *make_skillmod(skill_t sk, skillmod_fun special, double multiplier, int bonus) { attrib *a = a_new(&at_skillmod); skillmod_data *smd = (skillmod_data *)a->data.v; @@ -72,22 +71,19 @@ attrib *make_skillmod(skill_t sk, unsigned int flags, skillmod_fun special, smd->special = special; smd->bonus = bonus; smd->multiplier = multiplier; - smd->flags = flags; return a; } int -skillmod(const attrib * a, const unit * u, const region * r, skill_t sk, -int value, int flags) +skillmod(const unit * u, const region * r, skill_t sk, int value) { + const attrib * a = u->attribs; for (a = a_find((attrib *)a, &at_skillmod); a && a->type == &at_skillmod; a = a->next) { skillmod_data *smd = (skillmod_data *)a->data.v; if (smd->skill != NOSKILL && smd->skill != sk) continue; - if (flags != SMF_ALWAYS && (smd->flags & flags) == 0) - continue; if (smd->special) { value = smd->special(u, r, sk, value); if (value < 0) diff --git a/src/kernel/skills.h b/src/kernel/skills.h index 192030dcf..1261a7ea9 100644 --- a/src/kernel/skills.h +++ b/src/kernel/skills.h @@ -15,27 +15,21 @@ #include +struct race; +struct unit; +struct region; +struct attrib; +struct attrib_type; + #ifdef __cplusplus extern "C" { #endif - /* skillmod_data::flags -- wann gilt der modifier? */ -#define SMF_ALWAYS (1<<0) /* immer */ -#define SMF_PRODUCTION (1<<1) /* fr Produktion - am gebude, an der einheit */ -#define SMF_RIDING (1<<2) /* Bonus fr berittene - an der rasse */ - typedef struct skill { -#ifdef LOMEM - int id:8; - unsigned int level:8; - unsigned int weeks:8; - unsigned int old:8; -#else - int id; + skill_t id; int level; int weeks; int old; -#endif } skill; typedef int(*skillmod_fun) (const struct unit *, const struct region *, @@ -46,25 +40,23 @@ extern "C" { double multiplier; int number; int bonus; - int flags; } skillmod_data; - extern struct attrib_type at_skillmod; - extern int rc_skillmod(const struct race *rc, const struct region *r, - skill_t sk); - extern int skillmod(const struct attrib *a, const struct unit *u, - const struct region *r, skill_t sk, int value, int flags); - extern struct attrib *make_skillmod(skill_t sk, unsigned int flags, - skillmod_fun special, double multiplier, int bonus); - extern int level_days(int level); - extern int level(int days); + extern struct attrib_type at_skillmod; + + int rc_skillmod(const struct race *rc, const struct region *r, skill_t sk); + int skillmod(const struct unit *u, const struct region *r, skill_t sk, int value); + struct attrib *make_skillmod(skill_t sk, skillmod_fun special, double multiplier, int bonus); + + int level_days(int level); + int level(int days); #define skill_level(level) (level) - extern void reduce_skill(struct unit *u, skill * sv, unsigned int change); - extern int skill_weeks(int level); - extern int skill_compare(const skill * sk, const skill * sc); + void reduce_skill(struct unit *u, skill * sv, unsigned int change); + int skill_weeks(int level); + int skill_compare(const skill * sk, const skill * sc); - extern void sk_set(skill * sv, int level); + void sk_set(skill * sv, int level); #ifdef __cplusplus } diff --git a/src/kernel/spell.c b/src/kernel/spell.c index 212b0a7d2..0abb487f7 100644 --- a/src/kernel/spell.c +++ b/src/kernel/spell.c @@ -33,6 +33,51 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include + +static critbit_tree cb_spell_fun; +void add_spellcast(const char *sname, spell_f fun) +{ + size_t len; + char data[64]; + + len = cb_new_kv(sname, strlen(sname), &fun, sizeof(fun), data); + assert(len <= sizeof(data)); + cb_insert(&cb_spell_fun, data, len); +} + +spell_f get_spellcast(const char *sname) +{ + void * match; + spell_f result = NULL; + + if (cb_find_prefix(&cb_spell_fun, sname, strlen(sname) + 1, &match, 1, 0)) { + cb_get_kv(match, &result, sizeof(result)); + } + return result; +} + +static critbit_tree cb_fumble_fun; +void add_fumble(const char *sname, fumble_f fun) +{ + size_t len; + char data[64]; + + len = cb_new_kv(sname, strlen(sname), &fun, sizeof(fun), data); + assert(len <= sizeof(data)); + cb_insert(&cb_fumble_fun, data, len); +} + +fumble_f get_fumble(const char *sname) +{ + void * match; + fumble_f result = NULL; + + if (cb_find_prefix(&cb_fumble_fun, sname, strlen(sname) + 1, &match, 1, 0)) { + cb_get_kv(match, &result, sizeof(result)); + } + return result; +} + static critbit_tree cb_spells; selist * spells; @@ -49,6 +94,8 @@ static void free_spell_cb(void *cbdata) { } void free_spells(void) { + cb_clear(&cb_fumble_fun); + cb_clear(&cb_spell_fun); cb_clear(&cb_spells); selist_foreach(spells, free_spell_cb); selist_free(spells); @@ -62,7 +109,7 @@ void add_spell(struct selist **slistp, spell * sp) } } -spell * create_spell(const char * name, unsigned int id) +spell * create_spell(const char * name) { spell * sp; char buffer[64]; @@ -77,7 +124,6 @@ spell * create_spell(const char * name, unsigned int id) sp = (spell *)calloc(1, sizeof(spell)); len = cb_new_kv(name, len, &sp, sizeof(sp), buffer); if (cb_insert(&cb_spells, buffer, len) == CB_SUCCESS) { - sp->id = id ? id : hashstring(name); sp->sname = strdup(name); add_spell(&spells, sp); return sp; @@ -121,31 +167,6 @@ spell *find_spell(const char *name) return sp; } -spell *find_spellbyid(unsigned int id) -{ - selist *ql; - int qi; - - if (id == 0) - return NULL; - for (qi = 0, ql = spells; ql; selist_advance(&ql, &qi, 1)) { - spell *sp = (spell *)selist_get(ql, qi); - if (sp->id == id) { - return sp; - } - } - for (qi = 0, ql = spells; ql; selist_advance(&ql, &qi, 1)) { - spell *sp = (spell *)selist_get(ql, qi); - unsigned int hashid = hashstring(sp->sname); - if (hashid == id) { - return sp; - } - } - - log_warning("cannot find spell by id: %u\n", id); - return NULL; -} - struct spellref *spellref_create(spell *sp, const char *name) { spellref *spref = malloc(sizeof(spellref)); diff --git a/src/kernel/spell.h b/src/kernel/spell.h index e7592f082..17a565fa6 100644 --- a/src/kernel/spell.h +++ b/src/kernel/spell.h @@ -34,15 +34,12 @@ extern "C" { typedef void(*fumble_f)(const struct castorder * co); typedef struct spell { - unsigned int id; char *sname; char *syntax; char *parameter; int sptyp; int rank; /* Reihenfolge der Zauber */ struct spell_component *components; - spell_f cast; - fumble_f fumble; } spell; typedef struct spellref { @@ -50,15 +47,20 @@ extern "C" { struct spell *sp; } spellref; + void add_fumble(const char *sname, fumble_f fun); + fumble_f get_fumble(const char *sname); + + void add_spellcast(const char *sname, spell_f fun); + spell_f get_spellcast(const char *sname); + struct spellref *spellref_create(struct spell *sp, const char *name); void spellref_free(struct spellref *spref); struct spell *spellref_get(struct spellref *spref); int sp_antimagiczone(struct castorder *co); - struct spell * create_spell(const char * name, unsigned int id); + struct spell * create_spell(const char * name); struct spell * find_spell(const char *name); - struct spell * find_spellbyid(unsigned int i); void add_spell(struct selist **slistp, spell * sp); void free_spells(void); diff --git a/src/kernel/spell.test.c b/src/kernel/spell.test.c index b500f94cc..e5a37fd65 100644 --- a/src/kernel/spell.test.c +++ b/src/kernel/spell.test.c @@ -19,7 +19,7 @@ static void test_create_a_spell(CuTest * tc) CuAssertPtrEquals(tc, 0, spells); CuAssertPtrEquals(tc, 0, find_spell("testspell")); - sp = create_spell("testspell", 0); + sp = create_spell("testspell"); CuAssertPtrEquals(tc, sp, find_spell("testspell")); CuAssertPtrNotNull(tc, spells); test_cleanup(); @@ -37,8 +37,8 @@ static void test_create_duplicate_spell(CuTest * tc) CuAssertPtrEquals(tc, 0, find_spell("testspell")); - sp = create_spell("testspell", 0); - CuAssertPtrEquals(tc, 0, create_spell("testspell", 0)); + sp = create_spell("testspell"); + CuAssertPtrEquals(tc, 0, create_spell("testspell")); CuAssertPtrNotNull(tc, sl); CuAssertStrEquals(tc, "create_spell: duplicate name '%s'", sl->s); CuAssertPtrEquals(tc, 0, sl->next); @@ -47,28 +47,6 @@ static void test_create_duplicate_spell(CuTest * tc) test_cleanup(); } -static void test_create_spell_with_id(CuTest * tc) -{ - spell *sp; - struct log_t *log; - strlist *sl = 0; - - test_setup(); - test_log_stderr(0); - log = test_log_start(LOG_CPERROR, &sl); - - CuAssertPtrEquals(tc, 0, find_spellbyid(42)); - sp = create_spell("testspell", 42); - CuAssertPtrEquals(tc, sp, find_spellbyid(42)); - CuAssertPtrEquals(tc, 0, create_spell("testspell", 47)); - CuAssertPtrEquals(tc, 0, find_spellbyid(47)); - CuAssertPtrNotNull(tc, sl); - CuAssertStrEquals(tc, "create_spell: duplicate name '%s'", sl->s); - CuAssertPtrEquals(tc, 0, sl->next); - test_log_stop(log, sl); - test_cleanup(); -} - static void test_spellref(CuTest *tc) { spellref *ref; @@ -79,19 +57,39 @@ static void test_spellref(CuTest *tc) CuAssertPtrEquals(tc, NULL, ref->sp); CuAssertStrEquals(tc, "hodor", ref->name); CuAssertPtrEquals(tc, NULL, spellref_get(ref)); - sp = create_spell("hodor", 0); + sp = create_spell("hodor"); CuAssertPtrNotNull(tc, sp); CuAssertPtrEquals(tc, sp, spellref_get(ref)); spellref_free(ref); test_cleanup(); } +void fumble_foo(const struct castorder *co) { + UNUSED_ARG(co); +} + +void fumble_bar(const struct castorder *co) { + UNUSED_ARG(co); +} + +static void test_fumbles(CuTest *tc) +{ + test_setup(); + CuAssertTrue(tc, NULL==get_fumble("foo")); + add_fumble("foone", fumble_foo); + add_fumble("foozle", fumble_bar); + CuAssertTrue(tc, fumble_foo==get_fumble("foone")); + CuAssertTrue(tc, fumble_bar==get_fumble("foozle")); + CuAssertTrue(tc, NULL==get_fumble("foo")); + test_cleanup(); +} + CuSuite *get_spell_suite(void) { CuSuite *suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_spellref); + SUITE_ADD_TEST(suite, test_fumbles); SUITE_ADD_TEST(suite, test_create_a_spell); SUITE_ADD_TEST(suite, test_create_duplicate_spell); - SUITE_ADD_TEST(suite, test_create_spell_with_id); return suite; } diff --git a/src/kernel/spellbook.test.c b/src/kernel/spellbook.test.c index 8b997557b..37f20cced 100644 --- a/src/kernel/spellbook.test.c +++ b/src/kernel/spellbook.test.c @@ -33,7 +33,7 @@ void test_named_spellbooks(CuTest * tc) CuAssertPtrNotNull(tc, sb); CuAssertStrEquals(tc, "spells", sb->name); - sp = create_spell("testspell", 0); + sp = create_spell("testspell"); spellbook_add(sb, sp, 1); CuAssertPtrNotNull(tc, sb->spells); diff --git a/src/kernel/unit.c b/src/kernel/unit.c index 595accba2..ab5f72f35 100644 --- a/src/kernel/unit.c +++ b/src/kernel/unit.c @@ -438,8 +438,13 @@ int remove_unit(unit ** ulist, unit * u) *ulist = u->next; } - if (u->faction && u->faction->units == u) { - u->faction->units = u->nextF; + if (u->faction) { + if (count_unit(u)) { + --u->faction->num_units; + } + if (u->faction->units == u) { + u->faction->units = u->nextF; + } } if (u->prevF) { u->prevF->nextF = u->nextF; @@ -1125,12 +1130,13 @@ struct building *inside_building(const struct unit *u) void u_setfaction(unit * u, faction * f) { - int cnt = u->number; if (u->faction == f) return; if (u->faction) { - --u->faction->no_units; - set_number(u, 0); + if (count_unit(u)) { + --u->faction->num_units; + u->faction->num_people -= u->number; + } join_group(u, NULL); free_orders(&u->orders); set_order(&u->thisorder, NULL); @@ -1162,14 +1168,19 @@ void u_setfaction(unit * u, faction * f) if (u->region) { update_interval(f, u->region); } - if (cnt) { - set_number(u, cnt); - } - if (f) { - ++f->no_units; + if (f && count_unit(u)) { + ++f->num_units; + f->num_people += u->number; } } +bool count_unit(const unit *u) +{ + const race *rc = u_race(u); + /* spells are invisible. units we cannot see do not count to our limit */ + return rc == NULL || (rc->flags & RCF_INVISIBLE) == 0; +} + void set_number(unit * u, int count) { assert(count >= 0); @@ -1178,10 +1189,10 @@ void set_number(unit * u, int count) if (count == 0) { u->flags &= ~(UFL_HERO); } - if (u->faction) { + if (u->faction && count_unit(u)) { u->faction->num_people += count - u->number; } - u->number = (unsigned short)count; + u->number = count; } void remove_skill(unit * u, skill_t sk) @@ -1322,8 +1333,9 @@ int get_modifier(const unit * u, skill_t sk, int level, const region * r, bool n skill += rc_skillmod(u_race(u), r, sk); skill += att_modification(u, sk); - - skill = skillmod(u->attribs, u, r, sk, skill, SMF_ALWAYS); + if (u->attribs) { + skill = skillmod(u, r, sk, skill); + } if (fval(u, UFL_HUNGER)) { if (sk == SK_SAILING && skill > 2) { @@ -1484,6 +1496,8 @@ unit *create_unit(region * r, faction * f, int number, const struct race *urace, unit *u = (unit *)calloc(1, sizeof(unit)); assert(urace); + u_setrace(u, urace); + u->irace = NULL; if (f) { assert(faction_alive(f)); u_setfaction(u, f); @@ -1496,8 +1510,6 @@ unit *create_unit(region * r, faction * f, int number, const struct race *urace, } } } - u_setrace(u, urace); - u->irace = NULL; set_number(u, number); @@ -1581,7 +1593,7 @@ unit *create_unit(region * r, faction * f, int number, const struct race *urace, int maxheroes(const struct faction *f) { - int nsize = count_all(f); + int nsize = f->num_people; if (nsize == 0) return 0; else { @@ -1713,13 +1725,13 @@ int unit_max_hp(const unit * u) int h; double p; static int config; - static int rule_stamina; + static bool rule_stamina; h = u_race(u)->hitpoints; if (config_changed(&config)) { - rule_stamina = config_get_int("rules.stamina", STAMINA_AFFECTS_HP); + rule_stamina = config_get_int("rules.stamina", 1)!=0; } - if (rule_stamina & 1) { + if (rule_stamina) { p = pow(effskill(u, SK_STAMINA, u->region) / 2.0, 1.5) * 0.2; h += (int)(h * p + 0.5); } @@ -1803,7 +1815,23 @@ const struct race *u_race(const struct unit *u) void u_setrace(struct unit *u, const struct race *rc) { assert(rc); - u->_race = rc; + if (!u->faction) { + u->_race = rc; + } + else { + int n = 0; + if (count_unit(u)) { + --n; + } + u->_race = rc; + if (count_unit(u)) { + ++n; + } + if (n != 0) { + u->faction->num_units += n; + u->faction->num_people += n * u->number; + } + } } void unit_add_spell(unit * u, sc_mage * m, struct spell * sp, int level) diff --git a/src/kernel/unit.h b/src/kernel/unit.h index 14096c872..8ae13c5e2 100644 --- a/src/kernel/unit.h +++ b/src/kernel/unit.h @@ -130,6 +130,7 @@ extern "C" { int weight(const struct unit *u); void renumber_unit(struct unit *u, int no); + bool count_unit(const unit *u); /* unit counts towards faction.num_units and faction.num_people */ const struct race *u_irace(const struct unit *u); const struct race *u_race(const struct unit *u); diff --git a/src/kernel/unit.test.c b/src/kernel/unit.test.c index e1654b821..096da9d62 100644 --- a/src/kernel/unit.test.c +++ b/src/kernel/unit.test.c @@ -153,42 +153,37 @@ static void test_unit_name(CuTest *tc) { static void test_unit_name_from_race(CuTest *tc) { unit *u; - struct locale *lang; - test_cleanup(); - test_create_world(); - u = test_create_unit(test_create_faction(test_create_race("human")), findregion(0, 0)); + test_setup(); + u = test_create_unit(test_create_faction(test_create_race("human")), test_create_region(0, 0, NULL)); renumber_unit(u, 666); unit_setname(u, NULL); - lang = get_or_create_locale("de"); - locale_setstring(lang, rc_name_s(u->_race, NAME_SINGULAR), "Mensch"); - locale_setstring(lang, rc_name_s(u->_race, NAME_PLURAL), "Menschen"); - CuAssertStrEquals(tc, "Mensch (ii)", unitname(u)); - CuAssertStrEquals(tc, "Mensch", unit_getname(u)); + CuAssertStrEquals(tc, "human (ii)", unitname(u)); + CuAssertStrEquals(tc, "human", unit_getname(u)); u->number = 2; - CuAssertStrEquals(tc, "Menschen (ii)", unitname(u)); - CuAssertStrEquals(tc, "Menschen", unit_getname(u)); + CuAssertStrEquals(tc, "human_p (ii)", unitname(u)); + CuAssertStrEquals(tc, "human_p", unit_getname(u)); test_cleanup(); } static void test_update_monster_name(CuTest *tc) { unit *u; - struct locale *lang; + race *rc; - test_cleanup(); - test_create_world(); - u = test_create_unit(test_create_faction(test_create_race("human")), findregion(0, 0)); - lang = get_or_create_locale("de"); - locale_setstring(lang, rc_name_s(u->_race, NAME_SINGULAR), "Mensch"); - locale_setstring(lang, rc_name_s(u->_race, NAME_PLURAL), "Menschen"); + test_setup(); + rc = test_create_race("human"); + u = test_create_unit(test_create_faction(rc), test_create_region(0, 0, NULL)); unit_setname(u, "Hodor"); CuAssertTrue(tc, !unit_name_equals_race(u)); - unit_setname(u, "Menschling"); + unit_setname(u, "humanitarian"); + CuAssertTrue(tc, !unit_name_equals_race(u)); + + unit_setname(u, "huma"); CuAssertTrue(tc, !unit_name_equals_race(u)); unit_setname(u, rc_name_s(u->_race, NAME_SINGULAR)); @@ -197,12 +192,6 @@ static void test_update_monster_name(CuTest *tc) { unit_setname(u, rc_name_s(u->_race, NAME_PLURAL)); CuAssertTrue(tc, unit_name_equals_race(u)); - unit_setname(u, "Mensch"); - CuAssertTrue(tc, unit_name_equals_race(u)); - - unit_setname(u, "Menschen"); - CuAssertTrue(tc, unit_name_equals_race(u)); - test_cleanup(); } @@ -256,19 +245,19 @@ static void test_skillmod(CuTest *tc) { set_level(u, SK_ARMORER, 5); CuAssertIntEquals(tc, 5, effskill(u, SK_ARMORER, 0)); - a_add(&u->attribs, a = make_skillmod(SK_ARMORER, SMF_ALWAYS, 0, 2.0, 0)); + a_add(&u->attribs, a = make_skillmod(SK_ARMORER, 0, 2.0, 0)); CuAssertIntEquals(tc, 10, effskill(u, SK_ARMORER, 0)); a_remove(&u->attribs, a); - a_add(&u->attribs, a = make_skillmod(NOSKILL, SMF_ALWAYS, 0, 2.0, 0)); /* NOSKILL means any skill */ + a_add(&u->attribs, a = make_skillmod(NOSKILL, 0, 2.0, 0)); /* NOSKILL means any skill */ CuAssertIntEquals(tc, 10, effskill(u, SK_ARMORER, 0)); a_remove(&u->attribs, a); - a_add(&u->attribs, a = make_skillmod(SK_ARMORER, SMF_ALWAYS, 0, 0, 2)); + a_add(&u->attribs, a = make_skillmod(SK_ARMORER, 0, 0, 2)); CuAssertIntEquals(tc, 7, effskill(u, SK_ARMORER, 0)); a_remove(&u->attribs, a); - a_add(&u->attribs, a = make_skillmod(SK_ARMORER, SMF_ALWAYS, cb_skillmod, 0, 0)); + a_add(&u->attribs, a = make_skillmod(SK_ARMORER, cb_skillmod, 0, 0)); CuAssertIntEquals(tc, 8, effskill(u, SK_ARMORER, 0)); a_remove(&u->attribs, a); @@ -519,6 +508,43 @@ static void test_heal_factor(CuTest *tc) { test_cleanup(); } +static void test_unlimited_units(CuTest *tc) { + race *rc; + faction *f; + unit *u; + + test_setup(); + f = test_create_faction(NULL); + rc = test_create_race("spell"); + rc->flags |= RCF_INVISIBLE; + CuAssertIntEquals(tc, 0, f->num_units); + CuAssertIntEquals(tc, 0, f->num_people); + u = test_create_unit(f, test_create_region(0, 0, NULL)); + CuAssertTrue(tc, count_unit(u)); + CuAssertIntEquals(tc, 1, f->num_units); + CuAssertIntEquals(tc, 1, f->num_people); + u_setfaction(u, NULL); + CuAssertIntEquals(tc, 0, f->num_units); + CuAssertIntEquals(tc, 0, f->num_people); + u_setfaction(u, f); + CuAssertIntEquals(tc, 1, f->num_units); + CuAssertIntEquals(tc, 1, f->num_people); + u_setrace(u, rc); + CuAssertTrue(tc, !count_unit(u)); + CuAssertIntEquals(tc, 0, f->num_units); + CuAssertIntEquals(tc, 0, f->num_people); + scale_number(u, 10); + CuAssertIntEquals(tc, 0, f->num_units); + CuAssertIntEquals(tc, 0, f->num_people); + u_setrace(u, f->race); + CuAssertIntEquals(tc, 1, f->num_units); + CuAssertIntEquals(tc, 10, f->num_people); + remove_unit(&u->region->units, u); + CuAssertIntEquals(tc, 0, f->num_units); + CuAssertIntEquals(tc, 0, f->num_people); + test_cleanup(); +} + CuSuite *get_unit_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -534,6 +560,7 @@ CuSuite *get_unit_suite(void) SUITE_ADD_TEST(suite, test_remove_units_with_dead_faction); SUITE_ADD_TEST(suite, test_remove_empty_units_in_region); SUITE_ADD_TEST(suite, test_names); + SUITE_ADD_TEST(suite, test_unlimited_units); SUITE_ADD_TEST(suite, test_default_name); SUITE_ADD_TEST(suite, test_skillmod); SUITE_ADD_TEST(suite, test_skill_hunger); diff --git a/src/kernel/version.c b/src/kernel/version.c index 633fbf7bf..39f4f30e6 100644 --- a/src/kernel/version.c +++ b/src/kernel/version.c @@ -5,7 +5,7 @@ #ifndef ERESSEA_VERSION /* the version number, if it was not passed to make with -D */ -#define ERESSEA_VERSION "3.11.0" +#define ERESSEA_VERSION "3.12.0" #endif const char *eressea_version(void) { diff --git a/src/kernel/xmlreader.c b/src/kernel/xmlreader.c index 737a12f66..9953f7335 100644 --- a/src/kernel/xmlreader.c +++ b/src/kernel/xmlreader.c @@ -30,6 +30,7 @@ without prior permission by the authors of Eressea. #include "spellbook.h" #include "calendar.h" #include "prefix.h" +#include "move.h" #include "vortex.h" @@ -120,6 +121,72 @@ static xmlChar *xml_cleanup_string(xmlChar * str) return str; } +static resource_mod * xml_readmodifiers(xmlXPathObjectPtr result, xmlNodePtr node) { + /* reading eressea/resources/resource/modifier */ + if (result->nodesetval != NULL && result->nodesetval->nodeNr > 0) { + int k; + resource_mod * modifiers = + calloc(result->nodesetval->nodeNr + 1, sizeof(resource_mod)); + for (k = 0; k != result->nodesetval->nodeNr; ++k) { + xmlNodePtr node = result->nodesetval->nodeTab[k]; + xmlChar *propValue; + building_type *btype = NULL; + const race *rc = NULL; + + propValue = xmlGetProp(node, BAD_CAST "race"); + if (propValue != NULL) { + rc = rc_find((const char *)propValue); + if (rc == NULL) + rc = rc_get_or_create((const char *)propValue); + xmlFree(propValue); + } + modifiers[k].race = rc; + + propValue = xmlGetProp(node, BAD_CAST "building"); + if (propValue != NULL) { + btype = bt_get_or_create((const char *)propValue); + xmlFree(propValue); + } + modifiers[k].btype = btype; + + propValue = xmlGetProp(node, BAD_CAST "type"); + assert(propValue != NULL); + if (strcmp((const char *)propValue, "skill") == 0) { + xmlChar *propSkill; + skill_t sk = NOSKILL; + + modifiers[k].type = RMT_PROD_SKILL; + propSkill = xmlGetProp(node, BAD_CAST "skill"); + if (propSkill) { + sk = findskill((const char *)propSkill); + xmlFree(propSkill); + } + modifiers[k].value.sa[0] = (short)sk; + modifiers[k].value.sa[1] = (short)xml_ivalue(node, "value", 0); + } + else if (strcmp((const char *)propValue, "material") == 0) { + modifiers[k].value = xml_fraction(node, "value"); + modifiers[k].type = RMT_PROD_SAVE; + } + else { + if (strcmp((const char *)propValue, "require") == 0) { + modifiers[k].type = RMT_PROD_REQUIRE; + } + else if (strcmp((const char *)propValue, "save") == 0) { + modifiers[k].type = RMT_USE_SAVE; + modifiers[k].value = xml_fraction(node, "value"); + } + else { + log_error("unknown type '%s' for resourcelimit-modifier", (const char *)propValue); + } + } + xmlFree(propValue); + } + return modifiers; + } + return NULL; +} + static void xml_readrequirements(xmlNodePtr * nodeTab, int nodeNr, requirement ** reqArray) { @@ -148,7 +215,7 @@ xml_readrequirements(xmlNodePtr * nodeTab, int nodeNr, requirement ** reqArray) void xml_readconstruction(xmlXPathContextPtr xpath, xmlNodeSetPtr nodeSet, -construction ** consPtr) +construction ** consPtr, construct_t type) { xmlNodePtr pushNode = xpath->node; int k; @@ -157,7 +224,6 @@ construction ** consPtr) xmlChar *propValue; construction *con; xmlXPathObjectPtr req; - int m; skill_t sk = NOSKILL; propValue = xmlGetProp(node, BAD_CAST "skill"); @@ -176,15 +242,18 @@ construction ** consPtr) *consPtr = con = (construction *)calloc(sizeof(construction), 1); consPtr = &con->improvement; + con->type = type; con->skill = sk; con->maxsize = xml_ivalue(node, "maxsize", -1); con->minskill = xml_ivalue(node, "minskill", -1); con->reqsize = xml_ivalue(node, "reqsize", 1); - propValue = xmlGetProp(node, BAD_CAST "building"); - if (propValue != NULL) { - con->btype = bt_get_or_create((const char *)propValue); - xmlFree(propValue); + if (type == CONS_BUILDING) { + propValue = xmlGetProp(node, BAD_CAST "name"); + if (propValue != NULL) { + con->name = strdup((const char *)propValue); + xmlFree(propValue); + } } /* read construction/requirement */ @@ -193,23 +262,6 @@ construction ** consPtr) xml_readrequirements(req->nodesetval->nodeTab, req->nodesetval->nodeNr, &con->materials); xmlXPathFreeObject(req); - - /* read construction/modifier */ - xpath->node = node; - req = xmlXPathEvalExpression(BAD_CAST "modifier", xpath); - for (m = 0; m != req->nodesetval->nodeNr; ++m) { - xmlNodePtr node = req->nodesetval->nodeTab[m]; - - propValue = xmlGetProp(node, BAD_CAST "function"); - if (propValue != NULL) { - pf_generic foo = get_function((const char *)propValue); - a_add(&con->attribs, make_skillmod(NOSKILL, SMF_PRODUCTION, - (skillmod_fun)foo, 1.0, 0)); - xmlFree(propValue); - } - - } - xmlXPathFreeObject(req); } xpath->node = pushNode; } @@ -221,11 +273,8 @@ parse_function(xmlNodePtr node, pf_generic * funPtr, xmlChar ** namePtr) xmlChar *propValue = xmlGetProp(node, BAD_CAST "value"); assert(propValue != NULL); fun = get_function((const char *)propValue); - if (fun != NULL) { - xmlFree(propValue); - - propValue = xmlGetProp(node, BAD_CAST "name"); - } + xmlFree(propValue); + propValue = xmlGetProp(node, BAD_CAST "name"); *namePtr = propValue; *funPtr = fun; return 0; @@ -262,6 +311,7 @@ static int parse_buildings(xmlDocPtr doc) btype->magresbonus = xml_ivalue(node, "magresbonus", btype->magresbonus); btype->fumblebonus = xml_ivalue(node, "fumblebonus", btype->fumblebonus); btype->auraregen = xml_fvalue(node, "auraregen", btype->auraregen); + btype->taxes = xml_ivalue(node, "taxes", btype->taxes); if (xml_bvalue(node, "nodestroy", false)) btype->flags |= BTF_INDESTRUCTIBLE; @@ -280,10 +330,16 @@ static int parse_buildings(xmlDocPtr doc) if (xml_bvalue(node, "fort", false)) btype->flags |= BTF_FORTIFICATION; + /* reading eressea/buildings/building/modifier */ + xpath->node = node; + result = xmlXPathEvalExpression(BAD_CAST "modifier", xpath); + btype->modifiers = xml_readmodifiers(result, node); + xmlXPathFreeObject(result); + /* reading eressea/buildings/building/construction */ xpath->node = node; result = xmlXPathEvalExpression(BAD_CAST "construction", xpath); - xml_readconstruction(xpath, result->nodesetval, &btype->construction); + xml_readconstruction(xpath, result->nodesetval, &btype->construction, CONS_BUILDING); xmlXPathFreeObject(result); /* reading eressea/buildings/building/function */ @@ -300,23 +356,7 @@ static int parse_buildings(xmlDocPtr doc) continue; } assert(propValue != NULL); - if (strcmp((const char *)propValue, "name") == 0) { - btype->name = - (const char *(*)(const struct building_type *, - const struct building *, int))fun; - } - else if (strcmp((const char *)propValue, "init") == 0) { - btype->init = (void(*)(struct building_type *))fun; - } - else if (strcmp((const char *)propValue, "age") == 0) { - btype->age = (void(*)(struct building *))fun; - } - else if (strcmp((const char *)propValue, "taxes") == 0) { - btype->taxes = (double(*)(const struct building *, int))fun; - } - else { - log_error("unknown function type '%s' for building %s\n", (const char *)propValue, btype->_name); - } + log_error("unknown function type '%s' for building %s\n", (const char *)propValue, btype->_name); xmlFree(propValue); } xmlXPathFreeObject(result); @@ -344,10 +384,6 @@ static int parse_buildings(xmlDocPtr doc) mt->flags |= MTF_VARIABLE; } xmlXPathFreeObject(result); - - /* finally, initialize the new building type */ - if (btype->init) - btype->init(btype); } } xmlXPathFreeObject(buildings); @@ -356,132 +392,6 @@ static int parse_buildings(xmlDocPtr doc) return 0; } -static int parse_calendar(xmlDocPtr doc) -{ - xmlXPathContextPtr xpath = xmlXPathNewContext(doc); - xmlXPathObjectPtr xpathCalendars; - xmlNodeSetPtr nsetCalendars; - - xpathCalendars = xmlXPathEvalExpression(BAD_CAST "/eressea/calendar", xpath); - nsetCalendars = xpathCalendars->nodesetval; - if (nsetCalendars != NULL && nsetCalendars->nodeNr != 0) { - int c; - for (c = 0; c != nsetCalendars->nodeNr; ++c) { - xmlNodePtr calendar = nsetCalendars->nodeTab[c]; - xmlXPathObjectPtr xpathWeeks, xpathMonths, xpathSeasons; - xmlNodeSetPtr nsetWeeks, nsetMonths, nsetSeasons; - xmlChar *propValue = xmlGetProp(calendar, BAD_CAST "name"); - xmlChar *newyear = xmlGetProp(calendar, BAD_CAST "newyear"); - xmlChar *start; - - start = xmlGetProp(calendar, BAD_CAST "start"); - if (start && config_get("game.start") == NULL) { - config_set("game.start", (const char *)start); - xmlFree(start); - } - if (propValue) { - free(agename); - agename = strdup(mkname("calendar", (const char *)propValue)); - xmlFree(propValue); - } - - xpath->node = calendar; - xpathWeeks = xmlXPathEvalExpression(BAD_CAST "week", xpath); - nsetWeeks = xpathWeeks->nodesetval; - if (nsetWeeks != NULL && nsetWeeks->nodeNr) { - int i; - - weeks_per_month = nsetWeeks->nodeNr; - free(weeknames); - free(weeknames2); - weeknames = malloc(sizeof(char *) * weeks_per_month); - weeknames2 = malloc(sizeof(char *) * weeks_per_month); - for (i = 0; i != nsetWeeks->nodeNr; ++i) { - xmlNodePtr week = nsetWeeks->nodeTab[i]; - xmlChar *propValue = xmlGetProp(week, BAD_CAST "name"); - if (propValue) { - weeknames[i] = strdup(mkname("calendar", (const char *)propValue)); - weeknames2[i] = malloc(strlen(weeknames[i]) + 3); - sprintf(weeknames2[i], "%s_d", weeknames[i]); - xmlFree(propValue); - } - } - } - xmlXPathFreeObject(xpathWeeks); - - xpathSeasons = xmlXPathEvalExpression(BAD_CAST "season", xpath); - nsetSeasons = xpathSeasons->nodesetval; - if (nsetSeasons != NULL && nsetSeasons->nodeNr) { - int i; - - seasons = nsetSeasons->nodeNr; - assert(!seasonnames); - seasonnames = malloc(sizeof(char *) * seasons); - - for (i = 0; i != nsetSeasons->nodeNr; ++i) { - xmlNodePtr season = nsetSeasons->nodeTab[i]; - xmlChar *propValue = xmlGetProp(season, BAD_CAST "name"); - if (propValue) { - seasonnames[i] = - strdup(mkname("calendar", (const char *)propValue)); - xmlFree(propValue); - } - } - } - - xpathMonths = xmlXPathEvalExpression(BAD_CAST "season/month", xpath); - nsetMonths = xpathMonths->nodesetval; - if (nsetMonths != NULL && nsetMonths->nodeNr) { - int i; - - months_per_year = nsetMonths->nodeNr; - free(monthnames); - monthnames = malloc(sizeof(char *) * months_per_year); - free(month_season); - month_season = malloc(sizeof(int) * months_per_year); - free(storms); - storms = malloc(sizeof(int) * months_per_year); - - for (i = 0; i != nsetMonths->nodeNr; ++i) { - xmlNodePtr month = nsetMonths->nodeTab[i]; - xmlChar *propValue = xmlGetProp(month, BAD_CAST "name"); - - if (propValue) { - if (newyear - && strcmp((const char *)newyear, (const char *)propValue) == 0) { - first_month = i; - xmlFree(newyear); - newyear = NULL; - } - monthnames[i] = strdup(mkname("calendar", (const char *)propValue)); - xmlFree(propValue); - } - if (nsetSeasons) { - int j; - for (j = 0; j != seasons; ++j) { - xmlNodePtr season = month->parent; - if (season == nsetSeasons->nodeTab[j]) { - month_season[i] = j; - break; - } - } - assert(j != seasons); - } - storms[i] = xml_ivalue(nsetMonths->nodeTab[i], "storm", 0); - } - } - xmlXPathFreeObject(xpathMonths); - xmlXPathFreeObject(xpathSeasons); - xmlFree(newyear); - newyear = NULL; - } - } - xmlXPathFreeObject(xpathCalendars); - xmlXPathFreeContext(xpath); - - return 0; -} - static int parse_ships(xmlDocPtr doc) { xmlXPathContextPtr xpath = xmlXPathNewContext(doc); @@ -525,7 +435,7 @@ static int parse_ships(xmlDocPtr doc) /* reading eressea/ships/ship/construction */ xpath->node = node; result = xmlXPathEvalExpression(BAD_CAST "construction", xpath); - xml_readconstruction(xpath, result->nodesetval, &st->construction); + xml_readconstruction(xpath, result->nodesetval, &st->construction, CONS_OTHER); xmlXPathFreeObject(result); for (child = node->children; child; child = child->next) { @@ -757,6 +667,28 @@ static weapon_type *xml_readweapon(xmlXPathContextPtr xpath, item_type * itype) return wtype; } +static int race_mask = 1; + +static void mask_races(xmlNodePtr node, const char *key, int *maskp) { + xmlChar *propValue = xmlGetProp(node, BAD_CAST key); + int mask = 0; + assert(maskp); + if (propValue) { + char * tok = strtok((char *)propValue, " ,"); + while (tok) { + race * rc = rc_get_or_create(tok); + if (!rc->mask_item) { + rc->mask_item = race_mask; + race_mask = race_mask << 1; + } + mask |= rc->mask_item; + tok = strtok(NULL, " ,"); + } + xmlFree(propValue); + } + *maskp = mask; +} + static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype) { xmlNodePtr node = xpath->node; @@ -767,6 +699,8 @@ static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype) if (xml_bvalue(node, "cursed", false)) flags |= ITF_CURSED; + if (xml_bvalue(node, "use", false)) + flags |= ITF_CANUSE; if (xml_bvalue(node, "notlost", false)) flags |= ITF_NOTLOST; if (xml_bvalue(node, "herb", false)) @@ -780,12 +714,14 @@ static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype) itype = rtype->itype ? rtype->itype : it_get_or_create(rtype); itype->weight = xml_ivalue(node, "weight", 0); itype->capacity = xml_ivalue(node, "capacity", 0); + mask_races(node, "allow", &itype->mask_allow); + mask_races(node, "deny", &itype->mask_deny); itype->flags |= flags; /* reading item/construction */ xpath->node = node; result = xmlXPathEvalExpression(BAD_CAST "construction", xpath); - xml_readconstruction(xpath, result->nodesetval, &itype->construction); + xml_readconstruction(xpath, result->nodesetval, &itype->construction, CONS_ITEM); xmlXPathFreeObject(result); /* reading item/weapon */ @@ -803,6 +739,10 @@ static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype) result = xmlXPathEvalExpression(BAD_CAST "potion", xpath); assert(result->nodesetval->nodeNr <= 1); if (result->nodesetval->nodeNr != 0) { + if ((itype->flags & ITF_CANUSE) == 0) { + log_error("potion %s has no use attribute", rtype->_name); + itype->flags |= ITF_CANUSE; + } xpath->node = result->nodesetval->nodeTab[0]; rtype->ptype = xml_readpotion(xpath, itype); } @@ -843,28 +783,7 @@ static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype) continue; } assert(propValue != NULL); - if (strcmp((const char *)propValue, "give") == 0) { - itype->give = - (int(*)(struct unit *, struct unit *, const struct item_type *, int, - struct order *))fun; - } - else if (strcmp((const char *)propValue, "use") == 0) { - itype->use = - (int(*)(struct unit *, const struct item_type *, int, - struct order *))fun; - } - else if (strcmp((const char *)propValue, "canuse") == 0) { - itype->canuse = - (bool(*)(const struct unit *, const struct item_type *))fun; - } - else if (strcmp((const char *)propValue, "useonother") == 0) { - itype->useonother = - (int(*)(struct unit *, int, const struct item_type *, int, - struct order *))fun; - } - else { - log_error("unknown function type '%s' for item '%s'\n", (const char *)propValue, rtype->_name); - } + log_error("unknown function type '%s' for item '%s'\n", (const char *)propValue, rtype->_name); xmlFree(propValue); } itype->score = xml_ivalue(node, "score", 0); @@ -874,44 +793,6 @@ static item_type *xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype) return itype; } -static int parse_rules(xmlDocPtr doc) -{ - xmlXPathContextPtr xpath = xmlXPathNewContext(doc); - xmlXPathObjectPtr functions; - xmlNodeSetPtr nodes; - int i; - - /* reading eressea/resources/resource */ - functions = xmlXPathEvalExpression(BAD_CAST "/eressea/rules/function", xpath); - nodes = functions->nodesetval; - for (i = 0; i != nodes->nodeNr; ++i) { - xmlNodePtr node = nodes->nodeTab[i]; - xmlChar *propValue; - pf_generic fun; - - parse_function(node, &fun, &propValue); - - if (fun == NULL) { - log_error("unknown function for rule '%s' %s\n", (const char *)propValue); - xmlFree(propValue); - continue; - } - assert(propValue != NULL); - if (strcmp((const char *)propValue, "wage") == 0) { - global.functions.wage = - (int(*)(const struct region *, const struct faction *, - const struct race *, int))fun; - } - else { - log_error("unknown function for rule '%s'\n", (const char *)propValue); - } - xmlFree(propValue); - } - xmlXPathFreeObject(functions); - xmlXPathFreeContext(xpath); - return 0; -} - static int parse_resources(xmlDocPtr doc) { xmlXPathContextPtr xpath = xmlXPathNewContext(doc); @@ -933,8 +814,6 @@ static int parse_resources(xmlDocPtr doc) if (xml_bvalue(node, "pooled", true)) flags |= RTF_POOLED; - if (xml_bvalue(node, "limited", false)) - flags |= RTF_LIMITED; name = xmlGetProp(node, BAD_CAST "name"); if (!name) { @@ -965,9 +844,6 @@ static int parse_resources(xmlDocPtr doc) if (strcmp((const char *)propValue, "change") == 0) { rtype->uchange = (rtype_uchange)fun; } - else if (strcmp((const char *)propValue, "get") == 0) { - rtype->uget = (rtype_uget)fun; - } else if (strcmp((const char *)propValue, "name") == 0) { rtype->name = (rtype_name)fun; } @@ -982,102 +858,12 @@ static int parse_resources(xmlDocPtr doc) rmt_create(rtype); } - /* reading eressea/resources/resource/resourcelimit */ - xpath->node = node; - result = xmlXPathEvalExpression(BAD_CAST "resourcelimit", xpath); - assert(result->nodesetval->nodeNr <= 1); - if (result->nodesetval->nodeNr != 0) { - resource_limit *rdata = rtype->limit = calloc(1, sizeof(resource_limit)); - xmlNodePtr limit = result->nodesetval->nodeTab[0]; - + if (xml_bvalue(node, "limited", false)) { rtype->flags |= RTF_LIMITED; - xpath->node = limit; - xmlXPathFreeObject(result); - - result = xmlXPathEvalExpression(BAD_CAST "modifier", xpath); - if (result->nodesetval != NULL) { - rdata->modifiers = - calloc(result->nodesetval->nodeNr + 1, sizeof(resource_mod)); - for (k = 0; k != result->nodesetval->nodeNr; ++k) { - xmlNodePtr node = result->nodesetval->nodeTab[k]; - building_type *btype = NULL; - const race *rc = NULL; - - propValue = xmlGetProp(node, BAD_CAST "race"); - if (propValue != NULL) { - rc = rc_find((const char *)propValue); - if (rc == NULL) - rc = rc_get_or_create((const char *)propValue); - xmlFree(propValue); - } - rdata->modifiers[k].race = rc; - - propValue = xmlGetProp(node, BAD_CAST "building"); - if (propValue != NULL) { - btype = bt_get_or_create((const char *)propValue); - xmlFree(propValue); - } - rdata->modifiers[k].btype = btype; - - propValue = xmlGetProp(node, BAD_CAST "type"); - assert(propValue != NULL); - if (strcmp((const char *)propValue, "skill") == 0) { - rdata->modifiers[k].value.i = xml_ivalue(node, "value", 0); - rdata->modifiers[k].flags = RMF_SKILL; - } - else if (strcmp((const char *)propValue, "material") == 0) { - rdata->modifiers[k].value = xml_fraction(node, "value"); - rdata->modifiers[k].flags = RMF_SAVEMATERIAL; - } - else if (strcmp((const char *)propValue, "require") == 0) { - xmlChar *propBldg = xmlGetProp(node, BAD_CAST "building"); - if (propBldg != NULL) { - btype = bt_get_or_create((const char *)propBldg); - rdata->modifiers[k].btype = btype; - rdata->modifiers[k].flags = RMF_REQUIREDBUILDING; - xmlFree(propBldg); - } - } - else { - log_error("unknown type '%s' for resourcelimit-modifier '%s'\n", (const char *)propValue, rtype->_name); - } - xmlFree(propValue); - } - } - xmlXPathFreeObject(result); - - /* reading eressea/resources/resource/resourcelimit/function */ - result = xmlXPathEvalExpression(BAD_CAST "function", xpath); - if (result->nodesetval != NULL) { - for (k = 0; k != result->nodesetval->nodeNr; ++k) { - xmlNodePtr node = result->nodesetval->nodeTab[k]; - pf_generic fun; - - propValue = xmlGetProp(node, BAD_CAST "value"); - assert(propValue != NULL); - fun = get_function((const char *)propValue); - if (fun == NULL) { - log_error("unknown limit '%s' for resource %s\n", (const char *)propValue, rtype->_name); - xmlFree(propValue); - continue; - } - xmlFree(propValue); - - propValue = xmlGetProp(node, BAD_CAST "name"); - assert(propValue != NULL); - if (strcmp((const char *)propValue, "produce") == 0) { - rdata->produce = (rlimit_produce)fun; - } - else if (strcmp((const char *)propValue, "limit") == 0) { - rdata->limit = (rlimit_limit)fun; - } - else { - log_error("unknown limit '%s' for resource %s\n", (const char *)propValue, rtype->_name); - } - xmlFree(propValue); - } - } } + xpath->node = node; + result = xmlXPathEvalExpression(BAD_CAST "modifier", xpath); + rtype->modifiers = xml_readmodifiers(result, node); xmlXPathFreeObject(result); /* reading eressea/resources/resource/resourcelimit/function */ xpath->node = node; @@ -1383,12 +1169,8 @@ static int parse_spellbooks(xmlDocPtr doc) static int parse_spells(xmlDocPtr doc) { - pf_generic cast = 0; - pf_generic fumble = 0; xmlXPathContextPtr xpath = xmlXPathNewContext(doc); xmlXPathObjectPtr spells; - char zText[32]; - strcpy(zText, "fumble_"); /* reading eressea/spells/spell */ spells = xmlXPathEvalExpression(BAD_CAST "/eressea/spells/spell", xpath); @@ -1403,14 +1185,12 @@ static int parse_spells(xmlDocPtr doc) int k; spell_component *component; spell *sp; - unsigned int index; static int modes[] = { 0, PRECOMBATSPELL, COMBATSPELL, POSTCOMBATSPELL }; /* spellname */ - index = xml_ivalue(node, "index", 0); propValue = xmlGetProp(node, BAD_CAST "name"); assert(propValue != NULL); - sp = create_spell((const char *)propValue, index); + sp = create_spell((const char *)propValue); xmlFree(propValue); if (!sp) { continue; @@ -1463,50 +1243,9 @@ static int parse_spells(xmlDocPtr doc) sp->sptyp |= REGIONSPELL; k = xml_ivalue(node, "combat", 0); - if (k >= 0 && k <= 3) + if (k >= 0 && k <= 3) { sp->sptyp |= modes[k]; - - /* reading eressea/spells/spell/function */ - - xpath->node = node; - result = xmlXPathEvalExpression(BAD_CAST "function", xpath); - - if (result->nodesetval->nodeNr == 0) { - cast = get_function(sp->sname); - if (!cast) { - log_error("no spell cast function registered for '%s'\n", sp->sname); - } - strlcpy(zText + 7, sp->sname, sizeof(zText) - 7); - fumble = get_function(zText); } - else { - for (k = 0; k != result->nodesetval->nodeNr; ++k) { - xmlNodePtr node = result->nodesetval->nodeTab[k]; - pf_generic fun; - - parse_function(node, &fun, &propValue); - assert(propValue != NULL); - if (strcmp((const char *)propValue, "cast") == 0) { - if (fun) { - cast = fun; - } - else { - log_error("unknown function name '%s' for spell '%s'\n", (const char *)propValue, sp->sname); - } - } - else if (fun && strcmp((const char *)propValue, "fumble") == 0) { - fumble = fun; - } - else { - log_error("unknown function type '%s' for spell '%s'\n", (const char *)propValue, sp->sname); - } - xmlFree(propValue); - } - } - sp->cast = (spell_f)cast; - sp->fumble = (fumble_f)fumble; - xmlXPathFreeObject(result); - /* reading eressea/spells/spell/resource */ xpath->node = node; result = xmlXPathEvalExpression(BAD_CAST "resource", xpath); @@ -1997,10 +1736,7 @@ static int parse_strings(xmlDocPtr doc) void register_xmlreader(void) { - xml_register_callback(parse_rules); - xml_register_callback(parse_races); - xml_register_callback(parse_calendar); xml_register_callback(parse_resources); xml_register_callback(parse_buildings); /* requires resources */ diff --git a/src/kernel/xmlreader.h b/src/kernel/xmlreader.h index 5dfb643f1..290b785c3 100644 --- a/src/kernel/xmlreader.h +++ b/src/kernel/xmlreader.h @@ -12,6 +12,9 @@ without prior permission by the authors of Eressea. #ifndef H_KRNL_XMLREADER_H #define H_KRNL_XMLREADER_H + +struct spell; + #ifdef __cplusplus extern "C" { #endif diff --git a/src/laws.c b/src/laws.c index 1c71f1192..e16cbd583 100644 --- a/src/laws.c +++ b/src/laws.c @@ -45,6 +45,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* kernel includes */ #include #include +#include #include #include #include @@ -284,31 +285,6 @@ static double peasant_growth_factor(void) return config_get_flt("rules.peasants.growth.factor", 0.0001F * PEASANTGROWTH); } -#ifdef SLOWLUCK -int peasant_luck_effect(int peasants, int luck, int maxp, double variance) { - int n, births = 0; - double factor = peasant_growth_factor(); - for (n = peasants; n && luck; --n) { - int chances = 0; - - if (luck > 0) { - --luck; - chances += PEASANTLUCK; - } - - while (chances--) { - if (rng_double() < factor) { - /* Only raise with 75% chance if peasants have - * reached 90% of maxpopulation */ - if (peasants / (float)maxp < 0.9 || chance(PEASANTFORCE)) { - ++births; - } - } - } - } - return births; -} -#else static double peasant_luck_factor(void) { return config_get_flt("rules.peasants.peasantluck.factor", PEASANTLUCK); @@ -331,9 +307,7 @@ int peasant_luck_effect(int peasants, int luck, int maxp, double variance) return births; } -#endif - -static void peasants(region * r) +static void peasants(region * r, int rule) { int peasants = rpeasants(r); int money = rmoney(r); @@ -341,7 +315,7 @@ static void peasants(region * r) int n, satiated; int dead = 0; - if (peasants > 0 && config_get_int("rules.peasants.growth", 1)) { + if (peasants > 0 && rule > 0) { int luck = 0; double fraction = peasants * peasant_growth_factor(); int births = RAND_ROUND(fraction); @@ -812,6 +786,8 @@ void demographics(void) static int last_weeks_season = -1; static int current_season = -1; int plant_rules = config_get_int("rules.grow.formula", 2); + int horse_rules = config_get_int("rules.horses.growth", 1); + int peasant_rules = config_get_int("rules.peasants.growth", 1); const struct building_type *bt_harbour = bt_find("harbour"); if (current_season < 0) { @@ -843,7 +819,8 @@ void demographics(void) * und gewandert sind */ calculate_emigration(r); - peasants(r); + peasants(r, peasant_rules); + if (r->age > 20) { double mwp = MAX(region_maxworkers(r), 1); double prob = @@ -854,7 +831,9 @@ void demographics(void) plagues(r); } } - horses(r); + if (horse_rules > 0) { + horses(r); + } if (plant_rules == 2) { /* E2 */ growing_trees(r, current_season, last_weeks_season); growing_herbs(r, current_season, last_weeks_season); @@ -1274,8 +1253,8 @@ static void remove_idle_players(void) } else if (turn != f->lastorders) { char info[256]; - sprintf(info, "%d Einheiten, %d Personen, %d Silber", - f->no_units, f->num_total, f->money); + sprintf(info, "%d Einheiten, %d Personen", + f->num_units, f->num_people); } fp = &f->next; } @@ -1605,10 +1584,14 @@ int display_cmd(unit * u, struct order *ord) free(*s); if (s2) { - *s = strdup(s2); - if (strlen(s2) >= DISPLAYSIZE) { - (*s)[DISPLAYSIZE] = 0; + char * str = strdup(s2); + if (unicode_utf8_trim(str) != 0) { + log_info("trimming info: %s", s2); } + if (strlen(str) >= DISPLAYSIZE) { + str[DISPLAYSIZE-1] = 0; + } + *s = str; } else { *s = 0; @@ -1646,7 +1629,9 @@ static int rename_cmd(unit * u, order * ord, char **s, const char *s2) /* TODO: Validate to make sure people don't have illegal characters in * names, phishing-style? () come to mind. */ strlcpy(name, s2, sizeof(name)); - unicode_utf8_trim(name); + if (unicode_utf8_trim(name) != 0) { + log_info("trimming name: %s", s2); + } free(*s); *s = strdup(name); @@ -2405,6 +2390,7 @@ static void display_race(unit * u, const race * rc) static void reshow_other(unit * u, struct order *ord, const char *s) { int err = 21; + bool found = false; if (s) { const spell *sp = 0; @@ -2441,7 +2427,7 @@ static void reshow_other(unit * u, struct order *ord, const char *s) { else { display_item(u, itype); } - return; + found = true; } if (sp) { @@ -2452,15 +2438,16 @@ static void reshow_other(unit * u, struct order *ord, const char *s) { if (a != NULL) { a_remove(&u->faction->attribs, a); } - return; + found = true; } if (rc && u_race(u) == rc) { display_race(u, rc); - return; + found = true; } } - cmistake(u, ord, err, MSG_EVENT); + if (!found) + cmistake(u, ord, err, MSG_EVENT); } static void reshow(unit * u, struct order *ord, const char *s, param_t p) @@ -2514,7 +2501,7 @@ int promotion_cmd(unit * u, struct order *ord) u_race(u))); return 0; } - people = count_all(u->faction) * u->number; + people = u->faction->num_people * u->number; money = get_pooled(u, rsilver, GET_ALL, people); if (people > money) { @@ -2899,10 +2886,6 @@ static building *age_building(building * b) a_age(&b->attribs, b); handle_event(b->attribs, "timer", b); - if (b->type->age) { - b->type->age(b); - } - return b; } @@ -3015,8 +2998,7 @@ static int maxunits(const faction * f) int checkunitnumber(const faction * f, int add) { int alimit, flimit; - int flags = COUNT_DEFAULT | COUNT_MIGRANTS | COUNT_UNITS; - int fno = count_faction(f, flags) + add; + int fno = f->num_units + add; flimit = rule_faction_limit(); if (flimit && fno > flimit) { return 2; @@ -3024,22 +3006,10 @@ int checkunitnumber(const faction * f, int add) alimit = rule_alliance_limit(); if (alimit) { - /* if unitsperalliance is true, maxunits returns the - number of units allowed in an alliance */ - faction *f2; - int unitsinalliance = fno; - if (unitsinalliance > alimit) { + int unitsinalliance = alliance_size(f->alliance); + if (unitsinalliance + add > alimit) { return 1; } - - for (f2 = factions; f2; f2 = f2->next) { - if (f != f2 && f->alliance == f2->alliance) { - unitsinalliance += count_faction(f2, flags); - if (unitsinalliance > alimit) { - return 1; - } - } - } } return 0; @@ -3240,11 +3210,6 @@ void update_long_order(unit * u) static int use_item(unit * u, const item_type * itype, int amount, struct order *ord) { int i; - int target = -1; - - if (itype->useonother) { - target = read_unitid(u->faction, u->region); - } i = get_pooled(u, itype->rtype, GET_DEFAULT, amount); if (amount > i) { @@ -3255,19 +3220,14 @@ static int use_item(unit * u, const item_type * itype, int amount, struct order return ENOITEM; } - if (target == -1) { - if (itype->use) { - int result = itype->use(u, itype, amount, ord); - if (result > 0) { - use_pooled(u, itype->rtype, GET_DEFAULT, result); - } - return result; + if (itype->flags & ITF_CANUSE) { + int result = callbacks.use_item(u, itype, amount, ord); + if (result > 0) { + use_pooled(u, itype->rtype, GET_DEFAULT, result); } - return EUNUSABLE; - } - else { - return itype->useonother(u, target, itype, amount, ord); + return result; } + return EUNUSABLE; } void monthly_healing(void) @@ -4226,6 +4186,7 @@ static void reset_game(void) void turn_begin(void) { + assert(turn >= 0); ++turn; reset_game(); } @@ -4290,12 +4251,18 @@ void update_subscriptions(void) fclose(F); } -bool -cansee(const faction * f, const region * r, const unit * u, int modifier) -/* r kann != u->region sein, wenn es um Durchreisen geht, +/** determine if unit can be seen by faction + * @param f -- the observiong faction + * @param u -- the unit that is observed + * @param r -- the region that u is obesrved in (see below) + * @param m -- terrain modifier to stealth + * + * r kann != u->region sein, wenn es um Durchreisen geht, * oder Zauber (sp_generous, sp_fetchastral). * Es muss auch niemand aus f in der region sein, wenn sie vom Turm * erblickt wird */ +bool +cansee(const faction * f, const region * r, const unit * u, int modifier) { int stealth, rings; unit *u2 = r->units; diff --git a/src/laws.h b/src/laws.h index 090d2b978..7f1385483 100755 --- a/src/laws.h +++ b/src/laws.h @@ -58,6 +58,7 @@ extern "C" { void turn_process(void); void turn_end(void); + int checkunitnumber(const struct faction * f, int add); void new_units(void); void defaultorders(void); void quit(void); diff --git a/src/laws.test.c b/src/laws.test.c index 937754988..4d6adbfe9 100644 --- a/src/laws.test.c +++ b/src/laws.test.c @@ -5,6 +5,7 @@ #include "monsters.h" #include +#include #include #include #include @@ -427,7 +428,51 @@ static void test_unit_limit(CuTest * tc) test_cleanup(); } -extern int checkunitnumber(const faction * f, int add); +static void test_limit_new_units(CuTest * tc) +{ + faction *f; + unit *u; + alliance *al; + + test_setup(); + al = makealliance(1, "Hodor"); + f = test_create_faction(NULL); + u = test_create_unit(f, test_create_region(0, 0, NULL)); + CuAssertIntEquals(tc, 1, f->num_units); + CuAssertIntEquals(tc, 1, f->num_people); + scale_number(u, 10); + CuAssertIntEquals(tc, 10, f->num_people); + config_set("rules.limit.faction", "2"); + + u->orders = create_order(K_MAKETEMP, f->locale, "1"); + new_units(); + CuAssertPtrNotNull(tc, u->next); + CuAssertIntEquals(tc, 2, f->num_units); + + new_units(); + CuAssertIntEquals(tc, 2, f->num_units); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "too_many_units_in_faction")); + + setalliance(f, al); + + config_set("rules.limit.faction", "3"); + config_set("rules.limit.alliance", "2"); + + new_units(); + CuAssertIntEquals(tc, 2, f->num_units); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "too_many_units_in_alliance")); + + config_set("rules.limit.alliance", "3"); + u = test_create_unit(test_create_faction(NULL), u->region); + setalliance(u->faction, al); + + new_units(); + CuAssertIntEquals(tc, 2, f->num_units); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "too_many_units_in_alliance")); + + test_cleanup(); +} + static void test_cannot_create_unit_above_limit(CuTest * tc) { faction *f; @@ -480,10 +525,6 @@ struct pay_fixture { unit *u2; }; -static double level_taxes(const building * b, int level) { - return b->size * level * 2.0; -} - static void setup_pay_cmd(struct pay_fixture *fix) { faction *f; region *r; @@ -495,7 +536,7 @@ static void setup_pay_cmd(struct pay_fixture *fix) { r = findregion(0, 0); assert(r && f); btcastle = test_create_buildingtype("castle"); - btcastle->taxes = level_taxes; + btcastle->taxes = 100; b = test_create_building(r, btcastle); assert(b); fix->u1 = test_create_unit(f, r); @@ -1322,42 +1363,6 @@ static void test_show_without_item(CuTest *tc) test_cleanup(); } -static void test_show_elf(CuTest *tc) { - order *ord; - race * rc; - unit *u; - struct locale *loc; - message * msg; - - test_setup(); - - mt_register(mt_new_va("msg_event", "string:string", 0)); - rc = test_create_race("elf"); - test_create_itemtype("elvenhorse"); - - loc = test_create_locale(); - locale_setstring(loc, "elvenhorse", "Elfenpferd"); - locale_setstring(loc, "elvenhorse_p", "Elfenpferde"); - locale_setstring(loc, "race::elf_p", "Elfen"); - locale_setstring(loc, "race::elf", "Elf"); - init_locale(loc); - - CuAssertPtrNotNull(tc, finditemtype("elf", loc)); - CuAssertPtrNotNull(tc, findrace("elf", loc)); - - u = test_create_unit(test_create_faction(rc), test_create_region(0, 0, 0)); - u->faction->locale = loc; - ord = create_order(K_RESHOW, loc, "Elf"); - reshow_cmd(u, ord); - CuAssertTrue(tc, test_find_messagetype(u->faction->msgs, "error36") == NULL); - msg = test_find_messagetype(u->faction->msgs, "msg_event"); - CuAssertPtrNotNull(tc, msg); - CuAssertTrue(tc, memcmp("Elf:", msg->parameters[0].v, 4) == 0); - test_clear_messages(u->faction); - free_order(ord); - test_cleanup(); -} - static void test_show_race(CuTest *tc) { order *ord; race * rc; @@ -1399,15 +1404,52 @@ static void test_show_race(CuTest *tc) { test_cleanup(); } -static int low_wage(const region * r, const faction * f, const race * rc, int in_turn) { - return 1; +static void test_show_both(CuTest *tc) { + order *ord; + race * rc; + unit *u; + struct locale *loc; + message * msg; + + test_cleanup(); + + mt_register(mt_new_va("msg_event", "string:string", 0)); + mt_register(mt_new_va("displayitem", "weight:int", "item:resource", "description:string", 0)); + rc = test_create_race("elf"); + test_create_itemtype("elvenhorse"); + + loc = get_or_create_locale("de"); + locale_setstring(loc, "elvenhorse", "Elfenpferd"); + locale_setstring(loc, "elvenhorse_p", "Elfenpferde"); + locale_setstring(loc, "iteminfo::elvenhorse", "Hiyaa!"); + locale_setstring(loc, "race::elf_p", "Elfen"); + locale_setstring(loc, "race::elf", "Elf"); + init_locale(loc); + + CuAssertPtrNotNull(tc, finditemtype("elf", loc)); + CuAssertPtrNotNull(tc, findrace("elf", loc)); + + u = test_create_unit(test_create_faction(rc), test_create_region(0, 0, 0)); + u->faction->locale = loc; + i_change(&u->items, finditemtype("elfenpferd", loc), 1); + ord = create_order(K_RESHOW, loc, "Elf"); + reshow_cmd(u, ord); + CuAssertTrue(tc, test_find_messagetype(u->faction->msgs, "error36") == NULL); + msg = test_find_messagetype(u->faction->msgs, "msg_event"); + CuAssertPtrNotNull(tc, msg); + CuAssertTrue(tc, memcmp("Elf:", msg->parameters[0].v, 4) == 0); + msg = test_find_messagetype(u->faction->msgs, "displayitem"); + CuAssertPtrNotNull(tc, msg); + CuAssertTrue(tc, memcmp("Hiyaa!", msg->parameters[2].v, 4) == 0); + test_clear_messages(u->faction); + free_order(ord); + test_cleanup(); } static void test_immigration(CuTest * tc) { region *r; double inject[] = { 1 }; - int (*old_wage)(const region*, const faction*, const race*, int) = global.functions.wage; test_setup(); r = test_create_region(0, 0, 0); @@ -1427,10 +1469,9 @@ static void test_immigration(CuTest * tc) random_source_inject_array(inject, 2); - global.functions.wage = low_wage; + config_set("rules.wage.function", "0"); immigration(); CuAssertIntEquals(tc, 2, rpeasants(r)); - global.functions.wage = old_wage; test_cleanup(); } @@ -1535,6 +1576,7 @@ CuSuite *get_laws_suite(void) SUITE_ADD_TEST(suite, test_fishing_does_not_give_goblins_money); SUITE_ADD_TEST(suite, test_fishing_gets_reset); SUITE_ADD_TEST(suite, test_unit_limit); + SUITE_ADD_TEST(suite, test_limit_new_units); SUITE_ADD_TEST(suite, test_update_guards); SUITE_ADD_TEST(suite, test_newbie_cannot_guard); SUITE_ADD_TEST(suite, test_unarmed_cannot_guard); @@ -1571,8 +1613,8 @@ CuSuite *get_laws_suite(void) SUITE_ADD_TEST(suite, test_name_building); SUITE_ADD_TEST(suite, test_name_ship); SUITE_ADD_TEST(suite, test_show_without_item); - SUITE_ADD_TEST(suite, test_show_elf); SUITE_ADD_TEST(suite, test_show_race); + SUITE_ADD_TEST(suite, test_show_both); SUITE_ADD_TEST(suite, test_immigration); SUITE_ADD_TEST(suite, test_demon_hunger); SUITE_ADD_TEST(suite, test_armedmen); diff --git a/src/magic.c b/src/magic.c index de1336d66..81a797e1c 100644 --- a/src/magic.c +++ b/src/magic.c @@ -23,10 +23,12 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "skill.h" #include "study.h" +#include "helpers.h" #include "laws.h" #include #include +#include #include #include #include @@ -231,26 +233,6 @@ bool FactionSpells(void) return rule != 0; } -void read_spells(struct selist **slistp, magic_t mtype, - struct storage *store) -{ - for (;;) { - spell *sp; - char spname[64]; - - READ_TOK(store, spname, sizeof(spname)); - if (strcmp(spname, "end") == 0) - break; - sp = find_spell(spname); - if (!sp) { - log_error("read_spells: could not find spell '%s' in school '%s'\n", spname, magic_school[mtype]); - } - if (sp) { - add_spell(slistp, sp); - } - } -} - int get_spell_level_mage(const spell * sp, void * cbdata) { sc_mage *mage = (sc_mage *)cbdata; @@ -305,18 +287,6 @@ static int read_mage(attrib * a, void *owner, struct gamedata *data) return AT_READ_OK; } -void write_spells(struct selist *slist, struct storage *store) -{ - selist *ql; - int qi; - - for (ql = slist, qi = 0; ql; selist_advance(&ql, &qi, 1)) { - spell *sp = (spell *)selist_get(ql, qi); - WRITE_TOK(store, sp->sname); - } - WRITE_TOK(store, "end"); -} - static void write_mage(const attrib * a, const void *owner, struct storage *store) { @@ -371,23 +341,16 @@ sc_mage *get_mage(const unit * u) static int read_seenspell(attrib * a, void *owner, struct gamedata *data) { storage *store = data->store; - int i; spell *sp = 0; char token[32]; READ_TOK(store, token, sizeof(token)); - i = atoip(token); - if (i != 0) { - sp = find_spellbyid((unsigned int)i); + if (data->version < UNIQUE_SPELLS_VERSION) { + READ_INT(store, 0); /* ignore mtype */ } - else { - if (data->version < UNIQUE_SPELLS_VERSION) { - READ_INT(store, 0); /* ignore mtype */ - } - sp = find_spell(token); - if (!sp) { - log_warning("read_seenspell: could not find spell '%s'\n", token); - } + sp = find_spell(token); + if (!sp) { + log_warning("read_seenspell: could not find spell '%s'\n", token); } if (!sp) { return AT_READ_FAIL; @@ -930,9 +893,7 @@ void pay_spell(unit * u, const spell * sp, int cast_level, int range) bool knowsspell(const region * r, const unit * u, const spell * sp) { /* Ist überhaupt ein gültiger Spruch angegeben? */ - if (!sp || sp->id == 0) { - return false; - } + assert(sp); /* steht der Spruch in der Spruchliste? */ return u_hasspell(u, sp) != 0; } @@ -1146,7 +1107,7 @@ variant magic_resistance(unit * target) const plane *pl = rplane(target->region); if (rc == get_race(RC_HIRNTOETER) && !pl) { - prob.sa[1] *= 2; + prob = frac_mul(prob, frac_make(1, 2)); } assert(target->number > 0); /* Magier haben einen Resistenzbonus vom Magietalent * 5% */ @@ -1402,14 +1363,16 @@ static void do_fumble(castorder * co) double effect; static const race *rc_toad; static int rc_cache; + fumble_f fun; ADDMSG(&u->faction->msgs, msg_message("patzer", "unit region spell", u, r, sp)); switch (rng_int() % 10) { case 0: /* wenn vorhanden spezieller Patzer, ansonsten nix */ - if (sp->fumble) { - sp->fumble(co); + fun = get_fumble(sp->sname); + if (fun) { + fun(co); } else { fumble_default(co); @@ -1463,7 +1426,7 @@ static void do_fumble(castorder * co) case 5: case 6: /* Spruch gelingt, aber alle Magiepunkte weg */ - co->level = sp->cast(co); + co->level = cast_spell(co); set_spellpoints(u, 0); ADDMSG(&u->faction->msgs, msg_message("patzer4", "unit region spell", u, r, sp)); @@ -1474,7 +1437,7 @@ static void do_fumble(castorder * co) case 9: default: /* Spruch gelingt, alle nachfolgenden Sprüche werden 2^4 so teuer */ - co->level = sp->cast(co); + co->level = cast_spell(co); ADDMSG(&u->faction->msgs, msg_message("patzer5", "unit region spell", u, r, sp)); countspells(u, 3); @@ -2930,7 +2893,7 @@ void magic(void) fumbled = true; } else { - co->level = sp->cast(co); + co->level = cast_spell(co); if (co->level <= 0) { /* Kosten nur für real benötige Stufe berechnen */ continue; @@ -3033,6 +2996,30 @@ spell *unit_getspell(struct unit *u, const char *name, const struct locale * lan return 0; } +int cast_spell(struct castorder *co) +{ + const char *fname = co->sp->sname; + const char *hashpos = strchr(fname, '#'); + char fbuf[64]; + spell_f fun; + + const spell *sp = co->sp; + if (hashpos != NULL) { + ptrdiff_t len = hashpos - fname; + assert(len < (ptrdiff_t) sizeof(fbuf)); + memcpy(fbuf, fname, len); + fbuf[len] = '\0'; + fname = fbuf; + } + + fun = get_spellcast(sp->sname); + if (!fun) { + log_warning("no spell function for %s, try callback", sp->sname); + return callbacks.cast_spell(co, fname); + } + return fun(co); +} + static critbit_tree cb_spellbooks; spellbook * get_spellbook(const char * name) diff --git a/src/magic.h b/src/magic.h index 37fe33eae..6c905ea68 100644 --- a/src/magic.h +++ b/src/magic.h @@ -244,10 +244,6 @@ extern "C" { /* prft, ob der Spruch in der Spruchliste der Einheit steht. */ void pick_random_spells(struct faction *f, int level, struct spellbook * book, int num_spells); void show_new_spells(struct faction * f, int level, const struct spellbook *book); - void updatespelllist(struct unit *u); - /* fgt alle Zauber des Magiegebietes der Einheit, deren Stufe kleiner - * als das aktuelle Magietalent ist, in die Spruchliste der Einheit - * ein */ bool knowsspell(const struct region *r, const struct unit *u, const struct spell * sp); /* prft, ob die Einheit diesen Spruch gerade beherrscht, dh @@ -290,6 +286,7 @@ extern "C" { /* Hnge c-order co an die letze c-order von cll an */ void free_castorders(struct castorder *co); /* Speicher wieder freigeben */ + int cast_spell(struct castorder *co); /* Prfroutinen fr Zaubern */ int countspells(struct unit *u, int step); @@ -349,10 +346,6 @@ extern "C" { struct order *ord, const struct spllprm *spobj); bool FactionSpells(void); - void write_spells(struct selist *slist, struct storage *store); - void read_spells(struct selist **slistp, magic_t mtype, - struct storage *store); - struct spellbook * get_spellbook(const char * name); void free_spellbooks(void); void free_spellbook(struct spellbook *sb); diff --git a/src/magic.test.c b/src/magic.test.c index a3a199e42..a1b55b45d 100644 --- a/src/magic.test.c +++ b/src/magic.test.c @@ -4,7 +4,7 @@ #include "teleport.h" #include "give.h" -#include +#include #include #include #include @@ -14,13 +14,16 @@ #include #include #include -#include + +#include #include #include +#include #include #include +#include void test_updatespells(CuTest * tc) { @@ -32,7 +35,7 @@ void test_updatespells(CuTest * tc) test_create_race("human"); f = test_create_faction(0); - sp = create_spell("testspell", 0); + sp = create_spell("testspell"); CuAssertPtrNotNull(tc, sp); book = create_spellbook("spells"); @@ -65,7 +68,7 @@ void test_spellbooks(CuTest * tc) CuAssertStrEquals(tc, "herp", herp->name); CuAssertStrEquals(tc, "derp", derp->name); - sp = create_spell(sname, 0); + sp = create_spell(sname); spellbook_add(herp, sp, 1); CuAssertPtrNotNull(tc, sp); entry = spellbook_get(herp, sp); @@ -168,7 +171,7 @@ void test_getspell_unit(CuTest * tc) set_level(u, SK_MAGIC, 1); lang = test_create_locale(); - sp = create_spell("testspell", 0); + sp = create_spell("testspell"); locale_setstring(lang, mkname("spell", sp->sname), "Herp-a-derp"); CuAssertPtrEquals(tc, 0, unit_getspell(u, "Herp-a-derp", lang)); @@ -197,7 +200,7 @@ void test_getspell_faction(CuTest * tc) set_level(u, SK_MAGIC, 1); lang = test_create_locale(); - sp = create_spell("testspell", 0); + sp = create_spell("testspell"); locale_setstring(lang, mkname("spell", sp->sname), "Herp-a-derp"); CuAssertPtrEquals(tc, 0, unit_getspell(u, "Herp-a-derp", lang)); @@ -227,7 +230,7 @@ void test_getspell_school(CuTest * tc) set_level(u, SK_MAGIC, 1); lang = test_create_locale(); - sp = create_spell("testspell", 0); + sp = create_spell("testspell"); locale_setstring(lang, mkname("spell", sp->sname), "Herp-a-derp"); CuAssertPtrEquals(tc, 0, unit_getspell(u, "Herp-a-derp", lang)); @@ -254,7 +257,7 @@ void test_set_pre_combatspell(CuTest * tc) u = test_create_unit(f, r); enable_skill(SK_MAGIC, true); set_level(u, SK_MAGIC, 1); - sp = create_spell("testspell", 0); + sp = create_spell("testspell"); sp->sptyp |= PRECOMBATSPELL; unit_add_spell(u, 0, sp, 1); @@ -286,7 +289,7 @@ void test_set_main_combatspell(CuTest * tc) u = test_create_unit(f, r); enable_skill(SK_MAGIC, true); set_level(u, SK_MAGIC, 1); - sp = create_spell("testspell", 0); + sp = create_spell("testspell"); sp->sptyp |= COMBATSPELL; unit_add_spell(u, 0, sp, 1); @@ -318,7 +321,7 @@ void test_set_post_combatspell(CuTest * tc) u = test_create_unit(f, r); enable_skill(SK_MAGIC, true); set_level(u, SK_MAGIC, 1); - sp = create_spell("testspell", 0); + sp = create_spell("testspell"); sp->sptyp |= POSTCOMBATSPELL; unit_add_spell(u, 0, sp, 1); @@ -348,7 +351,7 @@ void test_hasspell(CuTest * tc) f->magiegebiet = M_TYBIED; u = test_create_unit(f, r); enable_skill(SK_MAGIC, true); - sp = create_spell("testspell", 0); + sp = create_spell("testspell"); sp->sptyp |= POSTCOMBATSPELL; unit_add_spell(u, 0, sp, 2); @@ -377,8 +380,8 @@ void test_multi_cast(CuTest *tc) { struct locale * lang; test_setup(); - sp = create_spell("fireball", 0); - sp->cast = cast_fireball; + add_spellcast("fireball", cast_fireball); + sp = create_spell("fireball"); CuAssertPtrEquals(tc, sp, find_spell("fireball")); lang = test_create_locale(); @@ -460,6 +463,27 @@ static void test_familiar_mage(CuTest *tc) { test_cleanup(); } +static void test_illusioncastle(CuTest *tc) +{ + building *b; + building_type *btype, *bt_icastle; + const attrib *a; + test_setup(); + btype = test_create_buildingtype("castle"); + bt_icastle = test_create_buildingtype("illusioncastle"); + b = test_create_building(test_create_region(0, 0, NULL), bt_icastle); + b->size = 1; + make_icastle(b, btype, 10); + a = a_find(b->attribs, &at_icastle); + CuAssertPtrNotNull(tc, a); + CuAssertPtrEquals(tc, btype, (void *)icastle_type(a)); + CuAssertPtrEquals(tc, bt_icastle, (void *)b->type); + CuAssertStrEquals(tc, "castle", buildingtype(btype, b, b->size)); + btype->construction->name = strdup("site"); + CuAssertStrEquals(tc, "site", buildingtype(btype, b, b->size)); + test_cleanup(); +} + CuSuite *get_magic_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -477,6 +501,7 @@ CuSuite *get_magic_suite(void) SUITE_ADD_TEST(suite, test_hasspell); SUITE_ADD_TEST(suite, test_magic_resistance); SUITE_ADD_TEST(suite, test_max_spellpoints); + SUITE_ADD_TEST(suite, test_illusioncastle); DISABLE_TEST(suite, test_familiar_mage); return suite; } diff --git a/src/main.c b/src/main.c index dd65ec9e1..eedb25cf1 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,5 @@ /* -Copyright (c) 1998-2015, Enno Rehling +Copyright (c) 1998-2017, Enno Rehling Katja Zedel @@ -45,33 +45,53 @@ static const char *inifile = "eressea.ini"; static int memdebug = 0; static int verbosity = 1; -static void load_inifile(dictionary * d) +static void load_inifile(void) { - const char *reportdir = reportpath(); - const char *datadir = datapath(); - const char *basedir = basepath(); const char *str; - assert(d); - - str = iniparser_getstring(d, "game:base", basedir); - if (str != basedir) { + str = config_get("game.base"); + if (str) { set_basepath(str); } - str = iniparser_getstring(d, "game:report", reportdir); - if (str != reportdir) { + str = config_get("game.report"); + if (str) { set_reportpath(str); } - str = iniparser_getstring(d, "game:data", datadir); - if (str != datadir) { + str = config_get("game.data"); + if (str) { set_datapath(str); } - lomem = iniparser_getint(d, "game:lomem", lomem) ? 1 : 0; - - verbosity = iniparser_getint(d, "game:verbose", 2); + lomem = config_get_int("game.lomem", lomem) ? 1 : 0; + verbosity = config_get_int("game.verbose", 2); + memdebug = config_get_int("game.memcheck", memdebug); +#ifdef USE_CURSES + /* only one value in the [editor] section */ + force_color = config_get_int("editor.color", force_color); + gm_codepage = config_get_int("editor.codepage", gm_codepage); +#endif } +static const char * valid_keys[] = { + "game.id", + "game.name", + "game.start", + "game.locale", + "game.verbose", + "game.report", + "game.lomem", + "game.memcheck", + "game.email", + "game.mailcmd", + "game.era", + "game.sender", + "editor.color", + "editor.codepage", + "editor.population.", + "lua.", + NULL +}; + static dictionary *parse_config(const char *filename) { char path[MAX_PATH]; @@ -87,15 +107,8 @@ static dictionary *parse_config(const char *filename) d = iniparser_load(filename); } if (d) { - load_inifile(d); - config_set_from(d); - - memdebug = iniparser_getint(d, "game:memcheck", memdebug); -#ifdef USE_CURSES - /* only one value in the [editor] section */ - force_color = iniparser_getint(d, "editor:color", force_color); - gm_codepage = iniparser_getint(d, "editor:codepage", gm_codepage); -#endif + config_set_from(d, valid_keys); + load_inifile(); } str = config_get("game.locales"); make_locales(str ? str : "de,en"); @@ -152,7 +165,7 @@ static int verbosity_to_flags(int verbosity) { return flags; } -static int parse_args(int argc, char **argv, int *exitcode) +static int parse_args(int argc, char **argv) { int i; int log_stderr = LOG_CPERROR; @@ -165,10 +178,10 @@ static int parse_args(int argc, char **argv, int *exitcode) } else if (argi[1] == '-') { /* long format */ if (strcmp(argi + 2, "version") == 0) { - printf("\n%s PBEM host\n" - "Copyright (C) 1996-2005 C. Schlittchen, K. Zedel, E. Rehling, H. Peters.\n\n" - "Compilation: " __DATE__ " at " __TIME__ "\nVersion: %s\n\n", - game_name(), eressea_version()); + printf("Eressea version %s, " + "Copyright (C) 2017 Enno Rehling et al.\n", + eressea_version()); + return 1; #ifdef USE_CURSES } else if (strcmp(argi + 2, "color") == 0) { @@ -216,7 +229,6 @@ static int parse_args(int argc, char **argv, int *exitcode) usage(argv[0], NULL); return 1; default: - *exitcode = -1; usage(argv[0], argi); return 1; } @@ -287,8 +299,10 @@ int main(int argc, char **argv) dictionary *d = 0; setup_signal_handler(); /* parse arguments again, to override ini file */ - parse_args(argc, argv, &err); - + err = parse_args(argc, argv); + if (err!=0) { + return (err>0) ? 0 : err; + } d = parse_config(inifile); if (!d) { log_error("could not open ini configuration %s\n", inifile); diff --git a/src/market.test.c b/src/market.test.c index a631dbcb7..2beeaea6b 100644 --- a/src/market.test.c +++ b/src/market.test.c @@ -43,9 +43,7 @@ static void test_market_curse(CuTest * tc) config_set("rules.region_owners", "1"); - btype = (building_type *)calloc(1, sizeof(building_type)); - btype->_name = strdup("market"); - bt_register(btype); + btype = bt_get_or_create("market"); terrain = get_terrain("plain"); diff --git a/src/modules/CMakeLists.txt b/src/modules/CMakeLists.txt index 633c486f5..82dee9ec4 100644 --- a/src/modules/CMakeLists.txt +++ b/src/modules/CMakeLists.txt @@ -1,6 +1,5 @@ PROJECT(modules C) SET(_FILES -arena.c autoseed.c gmcmd.c museum.c diff --git a/src/modules/arena.c b/src/modules/arena.c deleted file mode 100644 index aada12234..000000000 --- a/src/modules/arena.c +++ /dev/null @@ -1,255 +0,0 @@ -/* -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. -**/ - -#include -#include - -#if ARENA_MODULE -#include "arena.h" - -/* modules include */ -#include "score.h" - -/* items include */ -#include - -/* kernel includes */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* util include */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* libc include */ -#include -#include -#include -#include - -/* exports: */ -plane *arena = NULL; - -/* local vars */ -#define CENTRAL_VOLCANO 1 - -static region *tower_region[6]; -static region *start_region[6]; - -static region *arena_region(int school) -{ - return tower_region[school]; -} - -static building *arena_tower(int school) -{ - return arena_region(school)->buildings; -} - -static int leave_fail(unit * u) -{ - ADDMSG(&u->faction->msgs, msg_message("arena_leave_fail", "unit", u)); - return 1; -} - -static int -leave_arena(struct unit *u, const struct item_type *itype, int amount, -order * ord) -{ - if (!u->building && leave_fail(u)) { - return -1; - } - if (u->building != arena_tower(u->faction->magiegebiet) && leave_fail(u)) { - return -1; - } - UNUSED_ARG(amount); - UNUSED_ARG(ord); - UNUSED_ARG(itype); - assert(!"not implemented"); - return 0; -} - -static int enter_fail(unit * u) -{ - ADDMSG(&u->faction->msgs, msg_message("arena_enter_fail", "region unit", - u->region, u)); - return 1; -} - -static int -enter_arena(unit * u, const item_type * itype, int amount, order * ord) -{ - skill_t sk; - region *r = u->region; - unit *u2; - int fee = 2000; - UNUSED_ARG(ord); - UNUSED_ARG(amount); - UNUSED_ARG(itype); - if (u->faction->score > fee * 5) { - score_t score = u->faction->score / 5; - if (score < INT_MAX) { - fee = (int)score; - } - else { - fee = INT_MAX; - } - } - if (getplane(r) == arena) - return -1; - if (u->number != 1 && enter_fail(u)) - return -1; - if (get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT, fee) < fee - && enter_fail(u)) - return -1; - for (sk = 0; sk != MAXSKILLS; ++sk) { - if (get_level(u, sk) > 1 && enter_fail(u)) - return -1; - } - for (u2 = r->units; u2; u2 = u2->next) - if (u2->faction == u->faction) - break; - - assert(!"not implemented"); - /* - for (res=0;res!=MAX_RESOURCES;++res) if (res!=R_SILVER && res!=R_ARENA_GATE && (is_item(res) || is_herb(res) || is_potion(res))) { - int x = get_resource(u, res); - if (x) { - if (u2) { - change_resource(u2, res, x); - change_resource(u, res, -x); - } - else if (enter_fail(u)) return -1; - } - } - */ - if (get_money(u) > fee) { - if (u2) - change_money(u2, get_money(u) - fee); - else if (enter_fail(u)) - return -1; - } - ADDMSG(&u->faction->msgs, msg_message("arena_enter_fail", "region unit", - u->region, u)); - use_pooled(u, itype->rtype, GET_SLACK | GET_RESERVE, 1); - use_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT, fee); - set_money(u, 109); - fset(u, UFL_ANON_FACTION); - move_unit(u, start_region[rng_int() % 6], NULL); - return 0; -} -#ifdef CENTRAL_VOLCANO - -static int caldera_handle(trigger * t, void *data) -{ - /* call an event handler on caldera. - * data.v -> ( variant event, int timer ) - */ - building *b = (building *)t->data.v; - if (b != NULL) { - unit **up = &b->region->units; - while (*up) { - unit *u = *up; - if (u->building == b) { - message *msg; - if (u->items) { - item **ip = &u->items; - msg = msg_message("caldera_handle_1", "unit items", u, u->items); - while (*ip) { - item *i = *ip; - i_remove(ip, i); - if (*ip == i) - ip = &i->next; - } - } - else { - msg = msg_message("caldera_handle_0", "unit", u); - } - add_message(&u->region->msgs, msg); - set_number(u, 0); - } - if (*up == u) - up = &u->next; - } - } - else { - log_error("could not perform caldera::handle()\n"); - } - UNUSED_ARG(data); - return 0; -} - -static void caldera_write(const trigger * t, struct storage *store) -{ - building *b = (building *)t->data.v; - write_building_reference(b, store); -} - -static int caldera_read(trigger * t, struct gamedata *data) -{ - int rb = - read_reference(&t->data.v, data, read_building_reference, - resolve_building); - if (rb == 0 && !t->data.v) { - return AT_READ_FAIL; - } - return AT_READ_OK; -} - -struct trigger_type tt_caldera = { - "caldera", - NULL, - NULL, - caldera_handle, - caldera_write, - caldera_read -}; - -#endif - -void register_arena(void) -{ - at_deprecate("hurting", a_readint); - register_function((pf_generic)enter_arena, "enter_arena"); - register_function((pf_generic)leave_arena, "leave_arena"); - tt_register(&tt_caldera); -} - -#endif /* def ARENA_MODULE */ diff --git a/src/modules/arena.h b/src/modules/arena.h deleted file mode 100644 index 64bfc7e87..000000000 --- a/src/modules/arena.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -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. -**/ - -#ifndef ARENA_H -#define ARENA_H -#ifdef __cplusplus -extern "C" { -#endif - -#if ARENA_MODULE == 0 -#error "must define ARENA_MODULE to use this module" -#endif - /* exports: */ - extern struct plane *arena; - - extern void register_arena(void); -#ifdef ARENA_CREATION - extern void create_arena(void); -#endif - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/modules/gmcmd.c b/src/modules/gmcmd.c index 9a4e7ac47..c911e3de4 100644 --- a/src/modules/gmcmd.c +++ b/src/modules/gmcmd.c @@ -16,7 +16,6 @@ #include /* misc includes */ -#include #include #include diff --git a/src/modules/museum.c b/src/modules/museum.c index af37019d0..1eee3529f 100644 --- a/src/modules/museum.c +++ b/src/modules/museum.c @@ -497,9 +497,10 @@ void register_museum(void) at_register(&at_museumgivebackcookie); at_register(&at_museumgiveback); - register_item_use(use_museumticket, "use_museumticket"); - register_item_use(use_museumkey, "use_museumkey"); register_item_use(use_museumexitticket, "use_museumexitticket"); + register_item_use(use_museumticket, "use_museumticket"); + register_item_use(use_museumkey, "use_questkey1"); + register_item_use(use_museumkey, "use_questkey2"); } #endif diff --git a/src/modules/score.c b/src/modules/score.c index 0776d6507..efb3465d4 100644 --- a/src/modules/score.c +++ b/src/modules/score.c @@ -160,7 +160,7 @@ void score(void) faction *f; fwrite(utf8_bom, 1, 3, scoreFP); for (f = factions; f; f = f->next) - if (!fval(f, FFL_NPC) && f->num_total != 0) { + if (!fval(f, FFL_NPC) && f->num_people != 0) { char score[32]; write_score(score, sizeof(score), f->score); fprintf(scoreFP, "%s ", score); @@ -195,7 +195,7 @@ void score(void) if (f->alliance && f->alliance == a) { alliance_factions++; alliance_score += f->score; - alliance_number += f->num_total; + alliance_number += f->num_people; if (token != NULL) { unit *u = f->units; while (u != NULL) { diff --git a/src/monsters.c b/src/monsters.c index 0d9857024..62c982532 100644 --- a/src/monsters.c +++ b/src/monsters.c @@ -493,18 +493,12 @@ static order *make_movement_order(unit * u, const region * target, int moves, if (plan == NULL) return NULL; - bytes = - (int)strlcpy(bufp, - (const char *)LOC(u->faction->locale, keyword(K_MOVE)), size); - if (wrptr(&bufp, &size, bytes) != 0) - WARN_STATIC_BUFFER(); - while (position != moves && plan[position + 1]) { region *prev = plan[position]; region *next = plan[++position]; direction_t dir = reldirection(prev, next); assert(dir != NODIRECTION && dir != D_SPECIAL); - if (size > 1) { + if (size > 1 && bufp != zOrder) { *bufp++ = ' '; --size; } @@ -516,7 +510,7 @@ static order *make_movement_order(unit * u, const region * target, int moves, } *bufp = 0; - return parse_order(zOrder, u->faction->locale); + return create_order(K_MOVE, u->faction->locale, zOrder); } void random_growl(const unit *u, region *target, int rand) @@ -744,7 +738,7 @@ void plan_monsters(faction * f) order *long_order = NULL; /* Ab hier nur noch Befehle fr NPC-Einheiten. */ - if (!is_monsters(u->faction)) + if (u->faction!=f) continue; /* Befehle mssen jede Runde neu gegeben werden: */ @@ -754,10 +748,6 @@ void plan_monsters(faction * f) produceexp(u, SK_PERCEPTION, u->number); } - if (u->status > ST_BEHIND) { - setstatus(u, ST_FIGHT); - /* all monsters fight */ - } if (attacking && (!r->land || is_guard(u))) { monster_attacks(u, true, false); } @@ -805,10 +795,6 @@ void plan_monsters(faction * f) if (long_order == NULL) { /* Ab hier noch nicht generalisierte Spezialbehandlungen. */ - if (!u->orders) { - handle_event(u->attribs, "ai_move", u); - } - if (fval(rc, RCF_DRAGON)) { long_order = plan_dragon(u); } diff --git a/src/morale.c b/src/morale.c index 67108333e..8b0a8c6ee 100644 --- a/src/morale.c +++ b/src/morale.c @@ -43,10 +43,11 @@ void morale_update(region *r) { if (r->land->ownership && r->land->ownership->owner) { int stability = turn - r->land->ownership->morale_turn; int maxmorale = MORALE_DEFAULT; - building *b = largestbuilding(r, &cmp_taxes, false); + building *b = largestbuilding(r, cmp_taxes, false); if (b) { int bsize = buildingeffsize(b, false); - maxmorale = (int)(0.5 + b->type->taxes(b, bsize + 1) / MORALE_TAX_FACTOR); + assert(b->type->taxes>0); + maxmorale = (bsize + 1) * MORALE_TAX_FACTOR / b->type->taxes; } if (morale < maxmorale) { if (stability > MORALE_COOLDOWN && r->land->ownership->owner diff --git a/src/move.h b/src/move.h index 4d0b4e9f2..d5b07fef9 100644 --- a/src/move.h +++ b/src/move.h @@ -34,6 +34,7 @@ extern "C" { struct order; extern struct attrib_type at_shiptrail; + extern int *storms; /* die Zahlen sind genau quivalent zu den race Flags */ #define MV_CANNOTMOVE (1<<5) diff --git a/src/piracy.c b/src/piracy.c index 80b1dd47a..3025d484c 100644 --- a/src/piracy.c +++ b/src/piracy.c @@ -66,6 +66,8 @@ static attrib *mk_piracy(const faction * pirate, const faction * target, } static bool validate_pirate(unit *u, order *ord) { + assert(u); + assert(ord); if (fval(u_race(u), RCF_SWIM | RCF_FLY)) return true; if (!u->ship) { @@ -128,6 +130,7 @@ void piracy_cmd(unit * u) int saff = 0; int *il; + assert(u->thisorder); if (!validate_pirate(u, u->thisorder)) { return; } diff --git a/src/piracy.test.c b/src/piracy.test.c index 40e66af18..33860e03f 100644 --- a/src/piracy.test.c +++ b/src/piracy.test.c @@ -137,6 +137,7 @@ static void test_piracy_cmd_errors(CuTest * tc) { u_set_ship(u, test_create_ship(u->region, st_boat)); u2 = test_create_unit(u->faction, u->region); + u2->thisorder = create_order(K_PIRACY, f->locale, ""); u_set_ship(u2, u->ship); test_clear_messages(f); diff --git a/src/platform.h b/src/platform.h index 104ff28d7..9e2ecfadb 100644 --- a/src/platform.h +++ b/src/platform.h @@ -14,11 +14,14 @@ #define NO_STRDUP #define NO_MKDIR #define _CRT_SECURE_NO_WARNINGS +#define _USE_MATH_DEFINES +#if _MSC_VER >= 1900 #pragma warning(disable: 4710 4820) #pragma warning(disable: 4100) // unreferenced formal parameter #pragma warning(disable: 4456) // declaration hides previous #pragma warning(disable: 4457) // declaration hides function parameter #pragma warning(disable: 4459) // declaration hides global +#endif #else /* assume gcc */ #if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L # define va_copy(a,b) __va_copy(a,b) @@ -47,4 +50,10 @@ char * strdup(const char *s); int mkdir(const char *pathname, int mode); #endif +/* do not use M_PI, use one of these instead: */ +#define PI_F 3.1415926535897932384626433832795F +#define PI_D 3.1415926535897932384626433832795 +#define PI_L 3.1415926535897932384626433832795L + + #endif diff --git a/src/report.c b/src/report.c index 2f8ba7bce..188d8dfa9 100644 --- a/src/report.c +++ b/src/report.c @@ -123,13 +123,15 @@ static char *gamedate_season(const struct locale *lang) static char buf[256]; /* FIXME: static return value */ gamedate gd; + assert(weeknames); + get_gamedate(turn, &gd); sprintf(buf, (const char *)LOC(lang, "nr_calendar_season"), - LOC(lang, weeknames[gd.week]), - LOC(lang, monthnames[gd.month]), + LOC(lang, mkname("calendar", weeknames[gd.week])), + LOC(lang, mkname("calendar", calendar_month(gd.month))), gd.year, - agename ? LOC(lang, agename) : "", LOC(lang, seasonnames[gd.season])); + LOC(lang, mkname("calendar", calendar_era())), LOC(lang, mkname("calendar", seasonnames[gd.season]))); return buf; } @@ -2106,14 +2108,8 @@ report_plaintext(const char *filename, report_context * ctx, RENDER(f, buf, sizeof(buf), ("nr_score", "score average", score, avg)); centre(out, buf, true); } - no_units = count_units(f); - no_people = count_all(f); - if (f->flags & FFL_NPC) { - no_people = f->num_total; - } - else { - no_people = f->num_people; - } + no_units = f->num_units; + no_people = f->num_people; m = msg_message("nr_population", "population units limit", no_people, no_units, rule_faction_limit()); nr_render(m, f->locale, buf, sizeof(buf), f); msg_release(m); diff --git a/src/reports.c b/src/reports.c index fe1f59775..ba0302c6c 100644 --- a/src/reports.c +++ b/src/reports.c @@ -386,7 +386,7 @@ const char **illusion) if (illusion) { *illusion = NULL; - if (is_building_type(b->type, "illusioncastle")) { + if (b->attribs && is_building_type(b->type, "illusioncastle")) { const attrib *a = a_find(b->attribs, &at_icastle); if (a != NULL) { *illusion = buildingtype(icastle_type(a), b, b->size); @@ -1134,7 +1134,48 @@ void reports_done(void) { } } -static selist *get_regions_distance(region * root, int radius) +int get_regions_distance_arr(region *rc, int radius, region *result[], int size) +{ + int n = 0, i; + + if (size>n) { + result[n++] = rc; + fset(rc, RF_MARK); + } + for (i = 0; i != n; ++i) { + region *r; + int dist; + + r = result[i]; + dist = distance(rc, r); + if (dist n) { + if (!fval(r, RF_MARK) && dist < distance(rc, r)) { + result[n++] = r; + fset(r, RF_MARK); + } + } + else { + return -1; + } + } + } + } + } + for (i = 0; i != n; ++i) { + freset(result[i], RF_MARK); + } + return n; +} + +selist *get_regions_distance(region * root, int radius) { selist *ql, *rlist = NULL; int qi = 0; @@ -1180,7 +1221,7 @@ static void add_seen_nb(faction *f, region *r, seen_mode mode) { for (d = 0; d != MAXDIRECTIONS; ++d) { region *rn = next[d]; if (rn && rn->seen.modeseen.mode = seen_neighbour; if (first->index>rn->index) first = rn; if (last->indexindex) last = rn; } @@ -1192,9 +1233,8 @@ static void add_seen_nb(faction *f, region *r, seen_mode mode) { /** mark all regions seen by the lighthouse. */ -static void prepare_lighthouse(faction *f, region *r, int range) -{ - selist *ql, *rlist = get_regions_distance(r, range); +static void prepare_lighthouse_ql(faction *f, selist *rlist) { + selist *ql; int qi; for (ql = rlist, qi = 0; ql; selist_advance(&ql, &qi, 1)) { @@ -1203,7 +1243,28 @@ static void prepare_lighthouse(faction *f, region *r, int range) add_seen_nb(f, rl, seen_lighthouse); } } - selist_free(rlist); +} + +static void prepare_lighthouse(faction *f, region *r, int range) +{ + if (range > 3) { + selist *rlist = get_regions_distance(r, range); + prepare_lighthouse_ql(f, rlist); + selist_free(rlist); + } + else { + region *result[64]; + int n, i; + + n = get_regions_distance_arr(r, range, result, 64); + assert(n > 0 && n <= 64); + for (i = 0; i != n; ++i) { + region *rl = result[i]; + if (!fval(rl->terrain, FORBIDDEN_REGION)) { + add_seen_nb(f, rl, seen_lighthouse); + } + } + } } void reorder_units(region * r) diff --git a/src/reports.h b/src/reports.h index 6ce14e775..083248fd9 100644 --- a/src/reports.h +++ b/src/reports.h @@ -52,7 +52,8 @@ extern "C" { struct unit *can_find(struct faction *, struct faction *); bool omniscient(const struct faction *f); - + struct selist *get_regions_distance(struct region * root, int radius); + int get_regions_distance_arr(struct region *r, int radius, struct region *result[], int size); /* funktionen zum schreiben eines reports */ void sparagraph(struct strlist **SP, const char *s, unsigned int indent, char mark); void lparagraph(struct strlist **SP, char *s, unsigned int indent, char mark); diff --git a/src/reports.test.c b/src/reports.test.c index f66d94684..9d7944258 100644 --- a/src/reports.test.c +++ b/src/reports.test.c @@ -323,6 +323,7 @@ static void test_newbie_password_message(CuTest *tc) { prepare_report(&ctx, f); CuAssertIntEquals(tc, FFL_PWMSG, f->flags&FFL_PWMSG); CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "changepasswd")); + finish_reports(&ctx); test_cleanup(); } @@ -359,6 +360,7 @@ static void test_prepare_travelthru(CuTest *tc) { CuAssertPtrEquals(tc, f2, ctx.f); CuAssertPtrEquals(tc, r1, ctx.first); CuAssertPtrEquals(tc, NULL, ctx.last); + finish_reports(&ctx); test_cleanup(); } @@ -384,6 +386,7 @@ static void test_get_addresses(CuTest *tc) { CuAssertTrue(tc, selist_contains(ctx.addresses, f1, NULL)); CuAssertTrue(tc, selist_contains(ctx.addresses, f2, NULL)); CuAssertIntEquals(tc, 3, selist_length(ctx.addresses)); + finish_reports(&ctx); test_cleanup(); } @@ -411,6 +414,7 @@ static void test_get_addresses_fstealth(CuTest *tc) { CuAssertTrue(tc, !selist_contains(ctx.addresses, f1, NULL)); CuAssertTrue(tc, selist_contains(ctx.addresses, f2, NULL)); CuAssertIntEquals(tc, 2, selist_length(ctx.addresses)); + finish_reports(&ctx); test_cleanup(); } @@ -441,6 +445,7 @@ static void test_get_addresses_travelthru(CuTest *tc) { CuAssertTrue(tc, !selist_contains(ctx.addresses, f1, NULL)); CuAssertTrue(tc, selist_contains(ctx.addresses, f2, NULL)); CuAssertIntEquals(tc, 2, selist_length(ctx.addresses)); + finish_reports(&ctx); test_cleanup(); } @@ -523,6 +528,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); + finish_reports(&ctx); test_cleanup(); } @@ -560,6 +566,7 @@ static void test_prepare_lighthouse_owners(CuTest *tc) { CuAssertIntEquals(tc, seen_unit, r1->seen.mode); CuAssertIntEquals(tc, seen_lighthouse, r2->seen.mode); CuAssertIntEquals(tc, seen_neighbour, r3->seen.mode); + finish_reports(&ctx); test_cleanup(); } @@ -585,7 +592,6 @@ static void test_prepare_report(CuTest *tc) { CuAssertIntEquals(tc, seen_unit, r->seen.mode); finish_reports(&ctx); CuAssertIntEquals(tc, seen_none, r->seen.mode); - finish_reports(&ctx); r = test_create_region(2, 0, 0); CuAssertPtrEquals(tc, r, regions->next); @@ -593,6 +599,7 @@ static void test_prepare_report(CuTest *tc) { CuAssertPtrEquals(tc, regions, ctx.first); CuAssertPtrEquals(tc, r, ctx.last); CuAssertIntEquals(tc, seen_none, r->seen.mode); + finish_reports(&ctx); test_cleanup(); } @@ -644,9 +651,68 @@ static void test_seen_travelthru(CuTest *tc) { test_cleanup(); } +static void test_region_distance_max(CuTest *tc) { + region *r; + region *result[64]; + int x, y; + test_setup(); + r = test_create_region(0, 0, 0); + for (x=-3;x<=3;++x) { + for (y = -3; y <= 3; ++y) { + if (x != 0 || y != 0) { + test_create_region(x, y, 0); + } + } + } + CuAssertIntEquals(tc, 1, get_regions_distance_arr(r, 0, result, 64)); + CuAssertIntEquals(tc, 7, get_regions_distance_arr(r, 1, result, 64)); + CuAssertIntEquals(tc, 19, get_regions_distance_arr(r, 2, result, 64)); + CuAssertIntEquals(tc, 37, get_regions_distance_arr(r, 3, result, 64)); + test_cleanup(); +} + +static void test_region_distance(CuTest *tc) { + region *r; + region *result[8]; + test_setup(); + r = test_create_region(0, 0, 0); + CuAssertIntEquals(tc, 1, get_regions_distance_arr(r, 0, result, 8)); + CuAssertPtrEquals(tc, r, result[0]); + CuAssertIntEquals(tc, 1, get_regions_distance_arr(r, 1, result, 8)); + test_create_region(1, 0, 0); + test_create_region(0, 1, 0); + CuAssertIntEquals(tc, 1, get_regions_distance_arr(r, 0, result, 8)); + CuAssertIntEquals(tc, 3, get_regions_distance_arr(r, 1, result, 8)); + CuAssertIntEquals(tc, 3, get_regions_distance_arr(r, 2, result, 8)); + test_cleanup(); +} + +static void test_region_distance_ql(CuTest *tc) { + region *r; + selist *ql; + test_setup(); + r = test_create_region(0, 0, 0); + ql = get_regions_distance(r, 0); + CuAssertIntEquals(tc, 1, selist_length(ql)); + CuAssertPtrEquals(tc, r, selist_get(ql, 0)); + selist_free(ql); + test_create_region(1, 0, 0); + test_create_region(0, 1, 0); + ql = get_regions_distance(r, 1); + CuAssertIntEquals(tc, 3, selist_length(ql)); + selist_free(ql); + ql = get_regions_distance(r, 2); + CuAssertIntEquals(tc, 3, selist_length(ql)); + selist_free(ql); + test_cleanup(); +} + CuSuite *get_reports_suite(void) { CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_region_distance); + SUITE_ADD_TEST(suite, test_region_distance_max); + SUITE_ADD_TEST(suite, test_region_distance_ql); SUITE_ADD_TEST(suite, test_newbie_password_message); SUITE_ADD_TEST(suite, test_prepare_report); SUITE_ADD_TEST(suite, test_seen_neighbours); diff --git a/src/settings.h b/src/settings.h index b77e4687a..e8693a763 100644 --- a/src/settings.h +++ b/src/settings.h @@ -12,8 +12,6 @@ #define ENTERTAINFRACTION 20 #define TEACHDIFFERENCE 2 -#define GUARD_DISABLES_RECRUIT 1 -#define GUARD_DISABLES_PRODUCTION 1 #define RESOURCE_QUANTITY 0.5 #define RECRUITFRACTION 40 /* 100/RECRUITFRACTION% */ #define COMBAT_TURNS 5 @@ -31,27 +29,14 @@ * or both. We don't want separate binaries for different games */ #define MUSEUM_MODULE 1 -#define ARENA_MODULE 1 -#undef REGIONOWNERS /* (WIP) region-owner uses HELP_TRAVEL to control entry to region */ - - /* experimental gameplay features (that don't affect the savefile) */ /* TODO: move these settings to settings.h or into configuration files */ -#define GOBLINKILL /* Goblin-Spezialklau kann toedlich enden */ -#define INSECT_POTION /* Spezialtrank fuer Insekten */ - #define TREESIZE (8) /* space used by trees (in #peasants) */ - #define PEASANTFORCE 0.75 /* Chance einer Vermehrung trotz 90% Auslastung */ - /* Gebaeudegroesse = Minimalbelagerer */ +/* Gebaeudegroesse = Minimalbelagerer */ #define SIEGEFACTOR 2 - /** Magic */ -#define MAXMAGICIANS 3 -#define MAXALCHEMISTS 3 - #define ENCCHANCE 10 /* %-Chance fuer einmalige Zufallsbegegnung */ #define BAGCAPACITY 20000 /* soviel passt in einen Bag of Holding */ #define PERSON_WEIGHT 1000 /* weight of a "normal" human unit */ -#define STAMINA_AFFECTS_HP 1<<0 diff --git a/src/spells.c b/src/spells.c index fcee1b51b..b1e5c0591 100644 --- a/src/spells.c +++ b/src/spells.c @@ -523,7 +523,7 @@ static void make_familiar(unit * familiar, unit * mage) equip_items(&familiar->items, eq); } else { - log_error("could not perform initialization for familiar %s.\n", rc->_name); + log_info("could not perform initialization for familiar %s.\n", rc->_name); } /* triggers: */ @@ -1104,8 +1104,9 @@ static int sp_blessedharvest(castorder * co) if (create_curse(mage, &r->attribs, ct_find("blessedharvest"), co->force, duration, 1.0, 0)) { - message *seen = msg_message("harvest_effect", "mage", mage); - message *unseen = msg_message("harvest_effect", "mage", NULL); + const char * effect = co->sp->sname[0]=='b' ? "harvest_effect" : "raindance_effect"; + message *seen = msg_message(effect, "mage", mage); + message *unseen = msg_message(effect, "mage", NULL); report_effect(r, mage, seen, unseen); msg_release(seen); msg_release(unseen); @@ -2930,10 +2931,6 @@ static int dc_read_compat(struct attrib *a, void *target, gamedata *data) } return AT_READ_FAIL; /* we don't care for the attribute. */ } - -attrib_type at_deathcloud_compat = { - "zauber_todeswolke", NULL, NULL, NULL, NULL, dc_read_compat -}; #endif /* ------------------------------------------------------------- */ @@ -4537,12 +4534,7 @@ int sp_icastle(castorder * co) b->size = ((rng_int() % (int)(power)) + 1) * 5; } - if (type->name == NULL) { - bname = LOC(mage->faction->locale, type->_name); - } - else { - bname = LOC(mage->faction->locale, buildingtype(type, b, 0)); - } + bname = LOC(mage->faction->locale, buildingtype(type, b, 0)); building_setname(b, bname); /* TODO: Auf timeout und action_destroy umstellen */ @@ -6444,6 +6436,54 @@ int sp_break_curse(castorder * co) return cast_level; } +static int sp_flee(castorder *co) { + if (co->force <= 0) { + return 0; + } + return flee_spell(co, 4); +} + +static int sp_song_of_fear(castorder *co) { + if (co->force <= 0) { + return 0; + } + return flee_spell(co, 3); +} + +static int sp_aura_of_fear(castorder *co) { + if (co->force <= 0) { + return 0; + } + return flee_spell(co, 5); +} + +static int sp_armor_shield(struct castorder * co) { + return armor_spell(co, 3, 20); +} + +static int sp_bark_skin(struct castorder * co) { + return armor_spell(co, 4, 1); +} + +static int sp_kampfzauber(castorder *co) { + const spell * sp = co->sp; + if (co->force <= 0) { + return 0; + } + else if (strcmp(sp->sname, "fireball") == 0) { + return damage_spell(co, 0, 0); + } + else if (strcmp(sp->sname, "hail") == 0) { + return damage_spell(co, 2, 4); + } + else if (strcmp(sp->sname, "meteor_rain") == 0) { + return damage_spell(co, 1, 1); + } + else { + return damage_spell(co, 10, 10); + } +} + /* ------------------------------------------------------------- */ int sp_becomewyrm(castorder * co) { @@ -6451,183 +6491,6 @@ int sp_becomewyrm(castorder * co) return 0; } -typedef struct spelldata { - const char *sname; - spell_f cast; - fumble_f fumble; -} spelldata; - -static spelldata spell_functions[] = { - /* M_GWYRRD */ - { "stonegolem", sp_create_stonegolem, 0 }, - { "irongolem", sp_create_irongolem, 0 }, - { "treegrow", sp_hain, fumble_ents }, - { "rustweapon", sp_rosthauch, 0 }, - { "cold_protection", sp_kaelteschutz, 0 }, - { "ironkeeper", sp_ironkeeper, 0 }, - { "magicstreet", sp_magicstreet, 0 }, - { "windshield", sp_windshield, 0 }, - { "mallorntreegrow", sp_mallornhain, fumble_ents }, - { "goodwinds", sp_goodwinds, 0 }, - { "healing", sp_healing, 0 }, - { "reelingarrows", sp_reeling_arrows, 0 }, - { "gwyrrdfumbleshield", sp_fumbleshield, 0 }, - { "transferauradruide", sp_transferaura, 0 }, - { "earthquake", sp_earthquake, 0 }, - { "stormwinds", sp_stormwinds, 0 }, - { "homestone", sp_homestone, 0 }, - { "wolfhowl", sp_wolfhowl, 0 }, - { "igjarjuk", sp_igjarjuk, 0 }, - { "versteinern", sp_petrify, 0 }, - { "strongwall", sp_strong_wall, 0 }, - { "gwyrrddestroymagic", sp_destroy_magic, 0 }, - { "treewalkenter", sp_treewalkenter, 0 }, - { "treewalkexit", sp_treewalkexit, 0 }, - { "holyground", sp_holyground, 0 }, - { "summonent", sp_summonent, 0 }, - { "blessstonecircle", sp_blessstonecircle, 0 }, - { "barkskin", sp_armorshield, 0 }, - { "summonfireelemental", sp_drought, 0 }, - { "maelstrom", sp_maelstrom, 0 }, - { "magic_roots", sp_mallorn, 0 }, - { "great_drought", sp_great_drought, 0 }, - /* M_DRAIG */ - { "sparklechaos", sp_sparkle, 0 }, - { "magicboost", sp_magicboost, 0 }, - { "bloodsacrifice", sp_bloodsacrifice, 0 }, - { "berserk", sp_berserk, 0 }, - { "fumblecurse", sp_fumblecurse, patzer_fumblecurse }, - { "summonundead", sp_summonundead, patzer_peasantmob }, - { "combatrust", sp_combatrosthauch, 0 }, - { "transferaurachaos", sp_transferaura, 0 }, - { "firewall", sp_firewall, patzer_peasantmob }, - { "plague", sp_plague, patzer_peasantmob }, - { "chaosrow", sp_chaosrow, 0 }, - { "summonshadow", sp_summonshadow, patzer_peasantmob }, - { "undeadhero", sp_undeadhero, 0 }, - { "auraleak", sp_auraleak, 0 }, - { "draigfumbleshield", sp_fumbleshield, 0 }, - { "forestfire", sp_forest_fire, patzer_peasantmob }, - { "draigdestroymagic", sp_destroy_magic, 0 }, - { "unholypower", sp_unholypower, 0 }, - { "deathcloud", sp_deathcloud, patzer_peasantmob }, - { "summondragon", sp_summondragon, patzer_peasantmob }, - { "summonshadowlords", sp_summonshadowlords, patzer_peasantmob }, - { "chaossuction", sp_chaossuction, patzer_peasantmob }, - /* M_ILLAUN */ - { "sparkledream", sp_sparkle, 0 }, - { "shadowknights", sp_shadowknights, 0 }, - { "flee", sp_flee, 0 }, - { "puttorest", sp_puttorest, 0 }, - { "icastle", sp_icastle, 0 }, - { "transferauratraum", sp_transferaura, 0 }, - { "shapeshift", sp_illusionary_shapeshift, 0 }, - { "dreamreading", sp_dreamreading, 0 }, - { "tiredsoldiers", sp_tiredsoldiers, 0 }, - { "reanimate", sp_reanimate, 0 }, - { "analysedream", sp_analysedream, 0 }, - { "disturbingdreams", sp_disturbingdreams, 0 }, - { "sleep", sp_sleep, 0 }, - { "wisps", 0, 0 }, /* TODO: this spell is gone */ - { "gooddreams", sp_gooddreams, 0 }, - { "illaundestroymagic", sp_destroy_magic, 0 }, - { "clone", sp_clonecopy, 0 }, - { "bad_dreams", sp_baddreams, 0 }, - { "mindblast", sp_mindblast_temp, 0 }, - { "orkdream", sp_sweetdreams, 0 }, - /* M_CERDDOR */ - { "appeasement", sp_denyattack, 0 }, - { "song_of_healing", sp_healing, 0 }, - { "generous", sp_generous, 0 }, - { "song_of_fear", sp_flee, 0 }, - { "courting", sp_recruit, 0 }, - { "song_of_confusion", sp_chaosrow, 0 }, - { "heroic_song", sp_hero, 0 }, - { "transfer_aura_song", sp_transferaura, 0 }, - { "analysesong_unit", sp_analysesong_unit, 0 }, - { "cerddorfumbleshield", sp_fumbleshield, 0 }, - { "calm_monster", sp_calm_monster, 0 }, - { "seduction", sp_seduce, 0 }, - { "headache", sp_headache, 0 }, - { "sound_out", sp_pump, 0 }, - { "bloodthirst", sp_berserk, 0 }, - { "frighten", sp_frighten, 0 }, - { "analyse_object", sp_analysesong_obj, 0 }, - { "cerddor_destroymagic", sp_destroy_magic, 0 }, - { "migration", sp_migranten, 0 }, - { "summon_familiar", sp_summon_familiar, 0 }, - { "raise_mob", sp_raisepeasants, 0 }, - { "song_resist_magic", sp_song_resistmagic, 0 }, - { "melancholy", sp_depression, 0 }, - { "song_suscept_magic", sp_song_susceptmagic, 0 }, - { "song_of_peace", sp_song_of_peace, 0 }, - { "song_of_slavery", sp_charmingsong, 0 }, - { "big_recruit", sp_bigrecruit, 0 }, - { "calm_riot", sp_rallypeasantmob, 0 }, - { "incite_riot", sp_raisepeasantmob, 0 }, - /* M_TYBIED */ - { "analyze_magic", sp_analysemagic, 0 }, - { "concealing_aura", sp_itemcloak, 0 }, - { "tybiedfumbleshield", sp_fumbleshield, 0 }, -#ifdef SHOWASTRAL_NOT_BORKED - { "show_astral", sp_showastral, 0}, -#endif - { "resist_magic", sp_resist_magic_bonus, 0 }, - { "keeploot", sp_keeploot, 0 }, - { "enterastral", sp_enterastral, 0 }, - { "leaveastral", sp_leaveastral, 0 }, - { "auratransfer", sp_transferaura, 0 }, - { "shockwave", sp_stun, 0 }, - { "antimagiczone", sp_antimagiczone, 0 }, - { "destroy_magic", sp_destroy_magic, 0 }, - { "pull_astral", sp_pullastral, 0 }, - { "fetch_astral", sp_fetchastral, 0 }, - { "steal_aura", sp_stealaura, 0 }, - { "airship", sp_flying_ship, 0 }, - { "break_curse", sp_break_curse, 0 }, - { "eternal_walls", sp_eternizewall, 0 }, - { "protective_runes", sp_magicrunes, 0 }, - { "fish_shield", sp_reduceshield, 0 }, - { "combat_speed", sp_speed, 0 }, - { "view_reality", sp_viewreality, 0 }, - { "double_time", sp_speed2, 0 }, - { "armor_shield", sp_armorshield, 0 }, - { "living_rock", sp_movecastle, 0 }, - { "astral_disruption", sp_disruptastral, 0 }, - { "sacrifice_strength", sp_permtransfer, 0 }, - /* M_GRAY */ - /* Definitionen von Create_Artefaktspruechen */ - { "wyrm_transformation", sp_becomewyrm, 0 }, - /* Monstersprueche */ - { "fiery_dragonbreath", sp_dragonodem, 0 }, - { "icy_dragonbreath", sp_dragonodem, 0 }, - { "powerful_dragonbreath", sp_dragonodem, 0 }, - { "drain_skills", sp_dragonodem, 0 }, - { "aura_of_fear", sp_flee, 0 }, - { "immolation", sp_immolation, 0 }, - { "firestorm", sp_immolation, 0 }, - { "coldfront", sp_immolation, 0 }, - { "acidrain", sp_immolation, 0 }, - { 0, 0, 0 } -}; - -static void register_spelldata(void) -{ - int i; - char zText[32]; - strcpy(zText, "fumble_"); - for (i = 0; spell_functions[i].sname; ++i) { - spelldata *data = spell_functions + i; - if (data->cast) { - register_function((pf_generic)data->cast, data->sname); - } - if (data->fumble) { - strlcpy(zText + 7, data->sname, sizeof(zText) - 7); - register_function((pf_generic)data->fumble, zText); - } - } -} - /* ------------------------------------------------------------- */ /* Name: Plappermaul * Stufe: 4 @@ -6721,24 +6584,200 @@ static int sp_readmind(castorder * co) return cast_level; } -void register_magicresistance(void); +typedef struct spelldata { + const char *sname; + spell_f cast; + fumble_f fumble; +} spelldata; + +static spelldata spell_functions[] = { + /* M_GWYRRD */ + { "stonegolem", sp_create_stonegolem, 0 }, + { "irongolem", sp_create_irongolem, 0 }, + { "treegrow", sp_hain, fumble_ents }, + { "rustweapon", sp_rosthauch, 0 }, + { "cold_protection", sp_kaelteschutz, 0 }, + { "ironkeeper", sp_ironkeeper, 0 }, + { "magicstreet", sp_magicstreet, 0 }, + { "windshield", sp_windshield, 0 }, + { "mallorntreegrow", sp_mallornhain, fumble_ents }, + { "goodwinds", sp_goodwinds, 0 }, + { "healing", sp_healing, 0 }, + { "reelingarrows", sp_reeling_arrows, 0 }, + { "gwyrrdfumbleshield", sp_fumbleshield, 0 }, + { "transferauradruide", sp_transferaura, 0 }, + { "earthquake", sp_earthquake, 0 }, + { "stormwinds", sp_stormwinds, 0 }, + { "homestone", sp_homestone, 0 }, + { "wolfhowl", sp_wolfhowl, 0 }, + { "igjarjuk", sp_igjarjuk, 0 }, + { "versteinern", sp_petrify, 0 }, + { "strongwall", sp_strong_wall, 0 }, + { "gwyrrddestroymagic", sp_destroy_magic, 0 }, + { "treewalkenter", sp_treewalkenter, 0 }, + { "treewalkexit", sp_treewalkexit, 0 }, + { "holyground", sp_holyground, 0 }, + { "summonent", sp_summonent, 0 }, + { "blessstonecircle", sp_blessstonecircle, 0 }, + { "barkskin", sp_bark_skin, 0 }, + { "summonfireelemental", sp_drought, 0 }, + { "maelstrom", sp_maelstrom, 0 }, + { "magic_roots", sp_mallorn, 0 }, + { "great_drought", sp_great_drought, 0 }, + /* M_DRAIG */ + { "sparklechaos", sp_sparkle, 0 }, + { "magicboost", sp_magicboost, 0 }, + { "bloodsacrifice", sp_bloodsacrifice, 0 }, + { "berserk", sp_berserk, 0 }, + { "fumblecurse", sp_fumblecurse, patzer_fumblecurse }, + { "summonundead", sp_summonundead, patzer_peasantmob }, + { "combatrust", sp_combatrosthauch, 0 }, + { "transferaurachaos", sp_transferaura, 0 }, + { "firewall", sp_firewall, patzer_peasantmob }, + { "plague", sp_plague, patzer_peasantmob }, + { "chaosrow", sp_chaosrow, 0 }, + { "summonshadow", sp_summonshadow, patzer_peasantmob }, + { "undeadhero", sp_undeadhero, 0 }, + { "auraleak", sp_auraleak, 0 }, + { "draigfumbleshield", sp_fumbleshield, 0 }, + { "forestfire", sp_forest_fire, patzer_peasantmob }, + { "draigdestroymagic", sp_destroy_magic, 0 }, + { "unholypower", sp_unholypower, 0 }, + { "deathcloud", sp_deathcloud, patzer_peasantmob }, + { "summondragon", sp_summondragon, patzer_peasantmob }, + { "summonshadowlords", sp_summonshadowlords, patzer_peasantmob }, + { "chaossuction", sp_chaossuction, patzer_peasantmob }, + /* M_ILLAUN */ + { "sparkledream", sp_sparkle, 0 }, + { "shadowknights", sp_shadowknights, 0 }, + { "flee", sp_flee, 0 }, + { "puttorest", sp_puttorest, 0 }, + { "icastle", sp_icastle, 0 }, + { "transferauratraum", sp_transferaura, 0 }, + { "shapeshift", sp_illusionary_shapeshift, 0 }, + { "dreamreading", sp_dreamreading, 0 }, + { "tiredsoldiers", sp_tiredsoldiers, 0 }, + { "reanimate", sp_reanimate, 0 }, + { "analysedream", sp_analysedream, 0 }, + { "disturbingdreams", sp_disturbingdreams, 0 }, + { "sleep", sp_sleep, 0 }, + { "wisps", 0, 0 }, /* TODO: this spell is gone */ + { "gooddreams", sp_gooddreams, 0 }, + { "illaundestroymagic", sp_destroy_magic, 0 }, + { "clone", sp_clonecopy, 0 }, + { "bad_dreams", sp_baddreams, 0 }, + { "mindblast", sp_mindblast_temp, 0 }, + { "orkdream", sp_sweetdreams, 0 }, + /* M_CERDDOR */ + { "appeasement", sp_denyattack, 0 }, + { "song_of_healing", sp_healing, 0 }, + { "generous", sp_generous, 0 }, + { "song_of_fear", sp_song_of_fear, 0 }, + { "courting", sp_recruit, 0 }, + { "song_of_confusion", sp_chaosrow, 0 }, + { "heroic_song", sp_hero, 0 }, + { "transfer_aura_song", sp_transferaura, 0 }, + { "analysesong_unit", sp_analysesong_unit, 0 }, + { "cerddorfumbleshield", sp_fumbleshield, 0 }, + { "calm_monster", sp_calm_monster, 0 }, + { "seduction", sp_seduce, 0 }, + { "headache", sp_headache, 0 }, + { "sound_out", sp_pump, 0 }, + { "bloodthirst", sp_berserk, 0 }, + { "frighten", sp_frighten, 0 }, + { "analyse_object", sp_analysesong_obj, 0 }, + { "cerddor_destroymagic", sp_destroy_magic, 0 }, + { "migration", sp_migranten, 0 }, + { "summon_familiar", sp_summon_familiar, 0 }, + { "raise_mob", sp_raisepeasants, 0 }, + { "song_resist_magic", sp_song_resistmagic, 0 }, + { "melancholy", sp_depression, 0 }, + { "song_suscept_magic", sp_song_susceptmagic, 0 }, + { "song_of_peace", sp_song_of_peace, 0 }, + { "song_of_slavery", sp_charmingsong, 0 }, + { "big_recruit", sp_bigrecruit, 0 }, + { "calm_riot", sp_rallypeasantmob, 0 }, + { "incite_riot", sp_raisepeasantmob, 0 }, + /* M_TYBIED */ + { "analyze_magic", sp_analysemagic, 0 }, + { "concealing_aura", sp_itemcloak, 0 }, + { "tybiedfumbleshield", sp_fumbleshield, 0 }, +#ifdef SHOWASTRAL_NOT_BORKED + { "show_astral", sp_showastral, 0 }, +#endif + { "resist_magic", sp_resist_magic_bonus, 0 }, + { "keeploot", sp_keeploot, 0 }, + { "enterastral", sp_enterastral, 0 }, + { "leaveastral", sp_leaveastral, 0 }, + { "auratransfer", sp_transferaura, 0 }, + { "shockwave", sp_stun, 0 }, + { "antimagiczone", sp_antimagiczone, 0 }, + { "destroy_magic", sp_destroy_magic, 0 }, + { "pull_astral", sp_pullastral, 0 }, + { "fetch_astral", sp_fetchastral, 0 }, + { "steal_aura", sp_stealaura, 0 }, + { "airship", sp_flying_ship, 0 }, + { "break_curse", sp_break_curse, 0 }, + { "eternal_walls", sp_eternizewall, 0 }, + { "protective_runes", sp_magicrunes, 0 }, + { "fish_shield", sp_reduceshield, 0 }, + { "combat_speed", sp_speed, 0 }, + { "view_reality", sp_viewreality, 0 }, + { "double_time", sp_speed2, 0 }, + { "armor_shield", sp_armor_shield, 0 }, + { "living_rock", sp_movecastle, 0 }, + { "astral_disruption", sp_disruptastral, 0 }, + { "sacrifice_strength", sp_permtransfer, 0 }, + /* M_GRAY */ + /* Definitionen von Create_Artefaktspruechen */ + { "wyrm_transformation", sp_becomewyrm, 0 }, + /* Monstersprueche */ + { "fiery_dragonbreath", sp_dragonodem, 0 }, + { "icy_dragonbreath", sp_dragonodem, 0 }, + { "powerful_dragonbreath", sp_dragonodem, 0 }, + { "drain_skills", sp_dragonodem, 0 }, + { "aura_of_fear", sp_aura_of_fear, 0 }, + { "immolation", sp_immolation, 0 }, + { "firestorm", sp_immolation, 0 }, + { "coldfront", sp_immolation, 0 }, + { "acidrain", sp_immolation, 0 }, + { "blabbermouth", sp_babbler, NULL }, + { "summon_familiar", sp_summon_familiar, NULL }, + { "meteor_rain", sp_kampfzauber, NULL }, + { "fireball", sp_kampfzauber, NULL }, + { "hail", sp_kampfzauber, NULL }, + { "readmind", sp_readmind, NULL }, + { "blessedharvest", sp_blessedharvest, NULL }, + { "raindance", sp_blessedharvest, NULL }, + { 0, 0, 0 } +}; + +static void register_spelldata(void) +{ + int i; + for (i = 0; spell_functions[i].sname; ++i) { + spelldata *data = spell_functions + i; + if (data->cast) { + add_spellcast(data->sname, data->cast); + } + if (data->fumble) { + add_fumble(data->sname, data->fumble); + } + } +} void register_spells(void) { register_borders(); - at_register(&at_deathcloud_compat); +#ifdef COMPAT_DEATHCLOUD + at_deprecate("zauber_todeswolke", dc_read_compat); +#endif /* init_firewall(); */ ct_register(&ct_firewall); ct_register(&ct_deathcloud); - register_function((pf_generic)sp_blessedharvest, "cast_blessedharvest"); - register_function((pf_generic)sp_summon_familiar, "cast_familiar"); - register_function((pf_generic)sp_babbler, "cast_babbler"); - register_function((pf_generic)sp_readmind, "cast_readmind"); - register_function((pf_generic)sp_kampfzauber, "combat_spell"); - register_spelldata(); register_unitcurse(); diff --git a/src/spells.h b/src/spells.h index 65270a164..275f41f7a 100644 --- a/src/spells.h +++ b/src/spells.h @@ -27,6 +27,7 @@ extern "C" { struct unit; struct message; + void register_magicresistance(void); void register_spells(void); void set_spelldata(struct spell *sp); diff --git a/src/spells/combatspells.c b/src/spells/combatspells.c index 37191d177..08f38c8ea 100644 --- a/src/spells/combatspells.c +++ b/src/spells/combatspells.c @@ -47,31 +47,6 @@ #define EFFECT_HEALING_SPELL 5 -/* Some spells with a fixed, known ID (in XML). - * TODO: this method of identifying spells is error-prone, - * do not use it for new spells. */ -enum { - SPL_FIREBALL = 4, - SPL_HAGEL = 5, - SPL_CHAOSROW = 18, - SPL_FLEE = 20, - SPL_SONG_OF_FEAR = 21, - SPL_BERSERK = 22, - SPL_BLOODTHIRST = 23, - SPL_WINDSHIELD = 59, - SPL_HERO = 76, - SPL_METEORRAIN = 108, - SPL_REDUCESHIELD = 109, - SPL_ARMORSHIELD = 110, - SPL_DRAIG_FUMBLESHIELD = 143, - SPL_GWYRRD_FUMBLESHIELD = 144, - SPL_CERDDOR_FUMBLESHIELD = 145, - SPL_TYBIED_FUMBLESHIELD = 146, - SPL_SHADOWKNIGHTS = 147, - SPL_SHOCKWAVE = 163, - SPL_AURA_OF_FEAR = 175 -}; - /* ------------------------------------------------------------------ */ /* Kampfzauberfunktionen */ @@ -134,44 +109,23 @@ static double get_force(double power, int formel) } /* Generischer Kampfzauber */ -int sp_kampfzauber(struct castorder * co) +int damage_spell(struct castorder * co, int dmg, int strength) { fighter * fi = co->magician.fig; int level = co->level; - double power = co->force; const spell * sp = co->sp; + double power = co->force; battle *b = fi->side->battle; troop at, dt; message *m; /* Immer aus der ersten Reihe nehmen */ - int force, enemies; - int killed = 0; - const char *damage; + int enemies, killed = 0; + int force = lovar(get_force(power, strength)); + const char *damage = spell_damage(dmg); - if (power <= 0) - return 0; at.fighter = fi; at.index = 0; - switch (sp->id) { - /* lovar halbiert im Schnitt! */ - case SPL_FIREBALL: - damage = spell_damage(0); - force = lovar(get_force(power, 0)); - break; - case SPL_HAGEL: - damage = spell_damage(2); - force = lovar(get_force(power, 4)); - break; - case SPL_METEORRAIN: - damage = spell_damage(1); - force = lovar(get_force(power, 1)); - break; - default: - damage = spell_damage(10); - force = lovar(get_force(power, 10)); - } - enemies = count_enemies(b, fi, FIGHT_ROW, BEHIND_ROW - 1, SELECT_ADVANCE); if (enemies == 0) { message *m = @@ -257,13 +211,7 @@ int sp_stun(struct castorder * co) if (power <= 0) return 0; - switch (sp->id) { - case SPL_SHOCKWAVE: - force = lovar(get_force(power, 1)); - break; - default: - assert(0); - } + force = lovar(get_force(power, 1)); enemies = count_enemies(b, fi, FIGHT_ROW, BEHIND_ROW, SELECT_ADVANCE); if (!enemies) { @@ -556,7 +504,7 @@ int sp_mindblast_temp(struct castorder * co) skill_t sk = random_skill(du, true); if (sk != NOSKILL) { int n = 1 + rng_int() % maxloss; - attrib *a = make_skillmod(sk, SMF_ALWAYS, NULL, 0.0, n); + attrib *a = make_skillmod(sk, NULL, 0.0, n); /* neat: you can add a whole lot of these to a unit, they stack */ a_add(&du->attribs, a); } @@ -956,6 +904,7 @@ int sp_chaosrow(struct castorder * co) message *m; const char *mtype; int qi, k = 0; + bool chaosrow = strcmp(sp->sname, "chaosrow") == 0; if (!count_enemies(b, fi, FIGHT_ROW, NUMROWS, SELECT_ADVANCE | SELECT_FIND)) { m = msg_message("battle::out_of_range", "mage spell", fi->unit, sp); @@ -964,10 +913,7 @@ int sp_chaosrow(struct castorder * co) return 0; } - if (sp->id == SPL_CHAOSROW) - power *= 40; - else - power = get_force(power, 5); + power = chaosrow ? (power * 40) : get_force(power, 5); fgs = fighters(b, fi->side, FIGHT_ROW, NUMROWS, FS_ENEMY); scramble_fighters(fgs); @@ -1019,7 +965,7 @@ int sp_chaosrow(struct castorder * co) } selist_free(fgs); - if (sp->id == SPL_CHAOSROW) { + if (chaosrow) { mtype = (k > 0) ? "sp_chaosrow_effect_1" : "sp_chaosrow_effect_0"; } else { @@ -1034,33 +980,20 @@ int sp_chaosrow(struct castorder * co) /* Gesang der Furcht (Kampfzauber) */ /* Panik (Pr�kampfzauber) */ -int sp_flee(struct castorder * co) +int flee_spell(struct castorder * co, int strength) { fighter * fi = co->magician.fig; int level = co->level; - double power = co->force; const spell * sp = co->sp; battle *b = fi->side->battle; unit *mage = fi->unit; selist *fgs, *ql; - int force, n, qi; - int panik = 0; + int n, qi, panik = 0; message *msg; + double power = co->force; + int force; - switch (sp->id) { - case SPL_FLEE: - force = (int)get_force(power, 4); - break; - case SPL_SONG_OF_FEAR: - force = (int)get_force(power, 3); - break; - case SPL_AURA_OF_FEAR: - force = (int)get_force(power, 5); - break; - default: - force = (int)get_force(power, 10); - } - + force = (int)get_force(power, strength); if (!count_enemies(b, fi, FIGHT_ROW, AVOID_ROW, SELECT_ADVANCE | SELECT_FIND)) { msg = msg_message("sp_flee_effect_0", "mage spell", mage, sp); message_all(b, msg); @@ -1116,16 +1049,8 @@ int sp_hero(struct castorder * co) int targets = 0; message *m; - switch (sp->id) { - case SPL_HERO: - df_bonus = (int)(power / 5); - force = MAX(1, lovar(get_force(power, 4))); - break; - - default: - df_bonus = 1; - force = MAX(1, (int)power); - } + df_bonus = (int)(power / 5); + force = MAX(1, lovar(get_force(power, 4))); allies = count_allies(fi->side, FIGHT_ROW, BEHIND_ROW, SELECT_ADVANCE, ALLY_ANY); @@ -1170,19 +1095,9 @@ int sp_berserk(struct castorder * co) int targets = 0; message *m; - switch (sp->id) { - case SPL_BERSERK: - case SPL_BLOODTHIRST: - at_bonus = MAX(1, level / 3); - df_malus = 2; - force = (int)get_force(power, 2); - break; - - default: - at_bonus = 1; - df_malus = 0; - force = (int)power; - } + at_bonus = MAX(1, level / 3); + df_malus = 2; + force = (int)get_force(power, 2); allies = count_allies(fi->side, FIGHT_ROW, BEHIND_ROW - 1, SELECT_ADVANCE, ALLY_ANY); @@ -1327,16 +1242,9 @@ int sp_windshield(struct castorder * co) int enemies; message *m; - switch (sp->id) { - case SPL_WINDSHIELD: - force = (int)get_force(power, 4); - at_malus = level / 4; - break; + force = (int)get_force(power, 4); + at_malus = level / 4; - default: - force = (int)power; - at_malus = 2; - } enemies = count_enemies(b, fi, BEHIND_ROW, BEHIND_ROW, SELECT_ADVANCE); if (!enemies) { m = msg_message("battle::out_of_range", "mage spell", fi->unit, sp); @@ -1429,7 +1337,7 @@ static void do_meffect(fighter * af, int typ, int effect, int duration) me->duration = duration; } -int sp_armorshield(struct castorder * co) +int armor_spell(struct castorder * co, int per_level, int time_multi) { fighter * fi = co->magician.fig; int level = co->level; @@ -1445,16 +1353,8 @@ int sp_armorshield(struct castorder * co) /* gibt R�stung +effect f�r duration Treffer */ - switch (sp->id) { - case SPL_ARMORSHIELD: - effect = level / 3; - duration = (int)(20 * power * power); - break; - - default: - effect = level / 4; - duration = (int)(power * power); - } + effect = level / per_level; + duration = (int)(time_multi * power * power); do_meffect(fi, SHIELD_ARMOR, effect, duration); return level; } @@ -1475,16 +1375,9 @@ int sp_reduceshield(struct castorder * co) /* jeder Schaden wird um effect% reduziert bis der Schild duration * Trefferpunkte aufgefangen hat */ - switch (sp->id) { - case SPL_REDUCESHIELD: - effect = 50; - duration = (int)(50 * power * power); - break; + effect = 50; + duration = (int)(50 * power * power); - default: - effect = level * 3; - duration = (int)get_force(power, 5); - } do_meffect(fi, SHIELD_REDUCE, effect, duration); return level; } @@ -1503,20 +1396,9 @@ int sp_fumbleshield(struct castorder * co) msg_release(m); /* der erste Zauber schl�gt mit 100% fehl */ + duration = 100; + effect = MAX(1, 25 - level); - switch (sp->id) { - case SPL_DRAIG_FUMBLESHIELD: - case SPL_GWYRRD_FUMBLESHIELD: - case SPL_CERDDOR_FUMBLESHIELD: - case SPL_TYBIED_FUMBLESHIELD: - duration = 100; - effect = MAX(1, 25 - level); - break; - - default: - duration = 100; - effect = 10; - } do_meffect(fi, SHIELD_BLOCK, effect, duration); return level; } diff --git a/src/spells/combatspells.h b/src/spells/combatspells.h index cf07c2a89..78da949e8 100644 --- a/src/spells/combatspells.h +++ b/src/spells/combatspells.h @@ -23,12 +23,10 @@ extern "C" { int sp_fumbleshield(struct castorder * co); int sp_shadowknights(struct castorder * co); int sp_combatrosthauch(struct castorder * co); - int sp_kampfzauber(struct castorder * co); int sp_healing(struct castorder * co); int sp_keeploot(struct castorder * co); int sp_reanimate(struct castorder * co); int sp_chaosrow(struct castorder * co); - int sp_flee(struct castorder * co); int sp_berserk(struct castorder * co); int sp_tiredsoldiers(struct castorder * co); int sp_reeling_arrows(struct castorder * co); @@ -51,6 +49,10 @@ extern "C" { int sp_undeadhero(struct castorder * co); int sp_immolation(struct castorder * co); + int flee_spell(struct castorder * co, int strength); + int damage_spell(struct castorder * co, int dmg, int strength); + int armor_spell(struct castorder * co, int per_level, int time_multi); + #ifdef __cplusplus } #endif diff --git a/src/spy.c b/src/spy.c index 12a30848d..837bc28ee 100644 --- a/src/spy.c +++ b/src/spy.c @@ -199,8 +199,6 @@ static bool can_set_factionstealth(const unit * u, const faction * f) } ru = ru->next; } - if (ru != NULL) - break; } mu = mu->nextF; } @@ -249,17 +247,10 @@ int setstealth_cmd(unit * u, struct order *ord) if (trace) { /* demons can cloak as other player-races */ if (u_race(u) == get_race(RC_DAEMON)) { - race_t allowed[] = { RC_DWARF, RC_ELF, RC_ORC, RC_GOBLIN, RC_HUMAN, - RC_TROLL, RC_DAEMON, RC_INSECT, RC_HALFLING, RC_CAT, RC_AQUARIAN, - NORACE - }; - int i; - for (i = 0; allowed[i] != NORACE; ++i) { - if (get_race(allowed[i]) == trace) { - u->irace = trace; - if (u_race(u)->flags & RCF_SHAPESHIFTANY && get_racename(u->attribs)) - set_racename(&u->attribs, NULL); - break; + if (playerrace(trace)) { + u->irace = trace; + if (u_race(u)->flags & RCF_SHAPESHIFTANY && get_racename(u->attribs)) { + set_racename(&u->attribs, NULL); } } return 0; diff --git a/src/spy.test.c b/src/spy.test.c index 190cb09a5..11c8c1ff1 100644 --- a/src/spy.test.c +++ b/src/spy.test.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -130,21 +131,55 @@ static void test_sabotage_other_fail(CuTest *tc) { static void test_setstealth_cmd(CuTest *tc) { unit *u; const struct locale *lang; - + test_setup(); - u = test_create_unit(test_create_faction(0), test_create_region(0,0,0)); + u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); lang = u->faction->locale; - u->flags = UFL_ANON_FACTION|UFL_SIEGE; + u->flags = UFL_ANON_FACTION | UFL_SIEGE; u->thisorder = create_order(K_SETSTEALTH, lang, "%s %s", - LOC(lang, parameters[P_FACTION]), - LOC(lang, parameters[P_NOT])); + LOC(lang, parameters[P_FACTION]), + LOC(lang, parameters[P_NOT])); setstealth_cmd(u, u->thisorder); CuAssertIntEquals(tc, UFL_SIEGE, u->flags); free_order(u->thisorder); u->thisorder = create_order(K_SETSTEALTH, lang, "%s", - LOC(lang, parameters[P_FACTION])); + LOC(lang, parameters[P_FACTION])); setstealth_cmd(u, u->thisorder); - CuAssertIntEquals(tc, UFL_SIEGE|UFL_ANON_FACTION, u->flags); + CuAssertIntEquals(tc, UFL_SIEGE | UFL_ANON_FACTION, u->flags); + test_cleanup(); +} + +static void test_setstealth_demon(CuTest *tc) { + unit *u; + struct locale *lang; + struct race *rc; + + test_setup(); + lang = test_create_locale(); + rc = test_create_race("demon"); + u = test_create_unit(test_create_faction(rc), test_create_region(0, 0, 0)); + rc = test_create_race("dwarf"); + init_races(lang); + u->thisorder = create_order(K_SETSTEALTH, lang, racename(lang, u, rc)); + setstealth_cmd(u, u->thisorder); + CuAssertPtrEquals(tc, (void *)rc, (void *)u->irace); + test_cleanup(); +} + +static void test_setstealth_demon_bad(CuTest *tc) { + unit *u; + struct locale *lang; + struct race *rc; + + test_setup(); + lang = test_create_locale(); + rc = test_create_race("demon"); + u = test_create_unit(test_create_faction(rc), test_create_region(0, 0, 0)); + rc = test_create_race("smurf"); + init_races(lang); + u->thisorder = create_order(K_SETSTEALTH, lang, racename(lang, u, rc)); + setstealth_cmd(u, u->thisorder); + CuAssertPtrEquals(tc, NULL, (void *)u->irace); test_cleanup(); } @@ -180,6 +215,8 @@ CuSuite *get_spy_suite(void) SUITE_ADD_TEST(suite, test_all_spy_message); SUITE_ADD_TEST(suite, test_sabotage_self); SUITE_ADD_TEST(suite, test_setstealth_cmd); + SUITE_ADD_TEST(suite, test_setstealth_demon); + SUITE_ADD_TEST(suite, test_setstealth_demon_bad); SUITE_ADD_TEST(suite, test_sabotage_other_fail); SUITE_ADD_TEST(suite, test_sabotage_other_success); return suite; diff --git a/src/study.c b/src/study.c index 27443e996..9ea0a860e 100644 --- a/src/study.c +++ b/src/study.c @@ -163,6 +163,8 @@ static void init_learning(struct attrib *a) static void done_learning(struct attrib *a) { + teaching_info *teach = (teaching_info *)a->data.v; + selist_free(teach->teachers); free(a->data.v); } diff --git a/src/summary.c b/src/summary.c index 32416a263..90cab98c0 100644 --- a/src/summary.c +++ b/src/summary.c @@ -76,7 +76,6 @@ int update_nmrs(void) { int i, newplayers = 0; faction *f; - int turn = global.data_turn; int timeout = NMRTimeout(); if (timeout>0) { @@ -129,15 +128,15 @@ static char *rcomp(int i, int j) static void out_faction(FILE * file, const struct faction *f) { if (alliances != NULL) { - fprintf(file, "%s (%s/%d) (%.3s/%.3s), %d Einh., %d Pers., $%d, %d NMR\n", + fprintf(file, "%s (%s/%d) (%.3s/%.3s), %d Einh., %d Pers., %d NMR\n", f->name, itoa36(f->no), f_get_alliance(f) ? f->alliance->id : 0, LOC(default_locale, rc_name_s(f->race, NAME_SINGULAR)), magic_school[f->magiegebiet], - count_units(f), f->num_total, f->money, turn - f->lastorders); + f->num_units, f->num_people, turn - f->lastorders); } else { - fprintf(file, "%s (%.3s/%.3s), %d Einh., %d Pers., $%d, %d NMR\n", + fprintf(file, "%s (%.3s/%.3s), %d Einh., %d Pers., %d NMR\n", factionname(f), LOC(default_locale, rc_name_s(f->race, NAME_SINGULAR)), - magic_school[f->magiegebiet], count_units(f), f->num_total, f->money, + magic_school[f->magiegebiet], f->num_units, f->num_people, turn - f->lastorders); } } @@ -152,13 +151,12 @@ static char *gamedate2(const struct locale *lang) if (weeknames2) { week = weeknames2[gd.week]; } - if (monthnames) { - month = monthnames[gd.month]; - } + month = calendar_month(gd.month); sprintf(buf, "in %s des Monats %s im Jahre %d %s.", - LOC(lang, week), - LOC(lang, month), - gd.year, agename ? LOC(lang, agename) : ""); + LOC(lang, mkname("calendar", week)), + LOC(lang, mkname("calendar", month)), + gd.year, + LOC(lang, mkname("calendar", calendar_era()))); return buf; } @@ -393,9 +391,6 @@ summary *make_summary(void) plang->locale = lang; } ++plang->number; - f->nregions = 0; - f->num_total = 0; - f->money = 0; if (f->units) { s->factions++; /* Problem mit Monsterpartei ... */ @@ -434,11 +429,6 @@ summary *make_summary(void) s->peasants += rpeasants(r); s->peasantmoney += rmoney(r); - /* Einheiten Info. nregions darf nur einmal pro Partei - * incrementiert werden. */ - - for (u = r->units; u; u = u->next) - freset(u->faction, FFL_SELECT); for (u = r->units; u; u = u->next) { int orace; f = u->faction; @@ -471,14 +461,8 @@ summary *make_summary(void) if (aktskill > s->maxskill) s->maxskill = aktskill; } - if (!fval(f, FFL_SELECT)) { - f->nregions++; - fset(f, FFL_SELECT); - } } - f->num_total += u->number; - f->money += get_money(u); orace = (int)old_race(u_race(u)); if (orace >= 0) { s->poprace[orace] += u->number; diff --git a/src/test_eressea.c b/src/test_eressea.c index 1d991725f..5361ddf5e 100644 --- a/src/test_eressea.c +++ b/src/test_eressea.c @@ -116,35 +116,36 @@ int RunAllTests(int argc, char *argv[]) ADD_SUITE(ally); ADD_SUITE(messages); /* gamecode */ - ADD_SUITE(guard); - ADD_SUITE(report); - ADD_SUITE(creport); - ADD_SUITE(summary); - ADD_SUITE(names); ADD_SUITE(battle); - ADD_SUITE(volcano); + ADD_SUITE(calendar); + ADD_SUITE(creport); ADD_SUITE(donations); - ADD_SUITE(travelthru); ADD_SUITE(economy); ADD_SUITE(flyingship); ADD_SUITE(give); + ADD_SUITE(guard); + ADD_SUITE(key); ADD_SUITE(laws); ADD_SUITE(lighthouse); ADD_SUITE(market); ADD_SUITE(monsters); ADD_SUITE(move); + ADD_SUITE(names); + ADD_SUITE(otherfaction); ADD_SUITE(piracy); ADD_SUITE(prefix); ADD_SUITE(renumber); - ADD_SUITE(key); + ADD_SUITE(report); + ADD_SUITE(shock); + ADD_SUITE(spy); ADD_SUITE(stealth); - ADD_SUITE(otherfaction); + ADD_SUITE(study); + ADD_SUITE(summary); + ADD_SUITE(travelthru); ADD_SUITE(upkeep); + ADD_SUITE(volcano); ADD_SUITE(vortex); ADD_SUITE(wormhole); - ADD_SUITE(spy); - ADD_SUITE(study); - ADD_SUITE(shock); if (suites) { CuSuite *summary = CuSuiteNew(); diff --git a/src/tests.c b/src/tests.c index faa8f6e2e..ec2ac2a2f 100644 --- a/src/tests.c +++ b/src/tests.c @@ -3,6 +3,7 @@ #include "keyword.h" #include "prefix.h" #include "reports.h" +#include "calendar.h" #include #include @@ -96,8 +97,19 @@ struct locale * test_create_locale(void) { locale_setstring(loc, "stone_p", "Steine"); locale_setstring(loc, "plain", "Ebene"); locale_setstring(loc, "ocean", "Ozean"); - locale_setstring(loc, "race::human", "Mensch"); - locale_setstring(loc, "race::human_p", "Menschen"); + for (i = 0; i < MAXRACES; ++i) { + if (racenames[i]) { + char name[64]; + rc_key(racenames[i], NAME_PLURAL, name, sizeof(name)); + if (!locale_getstring(loc, name)) { + locale_setstring(loc, name, name + 6); + } + rc_key(racenames[i], NAME_SINGULAR, name, sizeof(name)); + if (!locale_getstring(loc, name)) { + locale_setstring(loc, name, name + 6); + } + } + } for (i = 0; i < MAXSKILLS; ++i) { if (!locale_getstring(loc, mkname("skill", skillnames[i]))) locale_setstring(loc, mkname("skill", skillnames[i]), skillnames[i]); @@ -181,7 +193,7 @@ void test_log_stderr(int flags) { static void test_reset(void) { int i; - turn = 0; + turn = 1; default_locale = 0; if (errno) { @@ -195,6 +207,7 @@ static void test_reset(void) { free_resources(); free_config(); default_locale = 0; + calendar_cleanup(); close_orders(); free_locales(); free_spells(); @@ -345,7 +358,7 @@ void test_create_castorder(castorder *co, unit *u, int level, float force, int r spell * test_create_spell(void) { spell *sp; - sp = create_spell("testspell", 0); + sp = create_spell("testspell"); sp->components = (spell_component *)calloc(4, sizeof(spell_component)); assert_alloc(sp->components); diff --git a/src/util/attrib.c b/src/util/attrib.c index 993f634d2..b3b9c0a6e 100644 --- a/src/util/attrib.c +++ b/src/util/attrib.c @@ -179,16 +179,6 @@ void at_register(attrib_type * at) at_hash[at->hashkey % MAXATHASH] = at; } -struct attrib_type *at_find(const char *name) { - attrib_type *find; - unsigned int hash = __at_hashkey(name); - find = at_hash[hash % MAXATHASH]; - while (find && hash != find->hashkey) { - find = find->nexthash; - } - return find; -} - static attrib_type *at_find_key(unsigned int hk) { const char *translate[3][2] = { @@ -210,6 +200,11 @@ static attrib_type *at_find_key(unsigned int hk) return find; } +struct attrib_type *at_find(const char *name) { + unsigned int hash = __at_hashkey(name); + return at_find_key(hash); +} + attrib *a_select(attrib * a, const void *data, bool(*compare) (const attrib *, const void *)) { @@ -220,6 +215,7 @@ attrib *a_select(attrib * a, const void *data, attrib *a_find(attrib * a, const attrib_type * at) { + assert(at); while (a && a->type != at) a = a->nexttype; return a; diff --git a/src/util/attrib.test.c b/src/util/attrib.test.c index b44afeec2..c59b8ae24 100644 --- a/src/util/attrib.test.c +++ b/src/util/attrib.test.c @@ -127,6 +127,7 @@ static void test_attrib_rwstring(CuTest *tc) { data.strm.api->rewind(data.strm.handle); a_readstring(&a, NULL, &data); CuAssertStrEquals(tc, "Hello World", (const char *)a.data.v); + a_finalizestring(&a); mstream_done(&data.strm); gamedata_done(&data); test_cleanup(); diff --git a/src/util/crmessage.c b/src/util/crmessage.c index 47bb6bf7d..dd400bf3d 100644 --- a/src/util/crmessage.c +++ b/src/util/crmessage.c @@ -81,7 +81,7 @@ static crmessage_type *crtypes[CRMAXHASH]; static crmessage_type *crt_find(const struct message_type *mtype) { - unsigned int hash = hashstring(mtype->name) % CRMAXHASH; + unsigned int hash = mtype->key % CRMAXHASH; crmessage_type *found = NULL; crmessage_type *type = crtypes[hash]; while (type) { @@ -94,7 +94,7 @@ static crmessage_type *crt_find(const struct message_type *mtype) void crt_register(const struct message_type *mtype) { - unsigned int hash = hashstring(mtype->name) % CRMAXHASH; + unsigned int hash = mtype->key % CRMAXHASH; crmessage_type *crt = crtypes[hash]; while (crt && crt->mtype != mtype) { crt = crt->next; diff --git a/src/util/gamedata.h b/src/util/gamedata.h index 75a750452..452a6e527 100644 --- a/src/util/gamedata.h +++ b/src/util/gamedata.h @@ -33,10 +33,11 @@ #define ATHASH_VERSION 353 /* attribute-type hash, not name */ #define NOWATCH_VERSION 354 /* plane->watchers is gone */ #define KEYVAL_VERSION 355 /* at_keys has values */ +#define NOLANDITEM_VERSION 356 /* land_region has no items */ /* unfinished: */ #define CRYPT_VERSION 400 /* passwords are encrypted */ -#define RELEASE_VERSION KEYVAL_VERSION /* current datafile */ +#define RELEASE_VERSION NOLANDITEM_VERSION /* current datafile */ #define MIN_VERSION INTPAK_VERSION /* minimal datafile we support */ #define MAX_VERSION RELEASE_VERSION /* change this if we can need to read the future datafile, and we can do so */ diff --git a/src/util/nrmessage.c b/src/util/nrmessage.c index 717c4dc1f..4b7c5ea75 100644 --- a/src/util/nrmessage.c +++ b/src/util/nrmessage.c @@ -40,7 +40,7 @@ nrmessage_type *nrt_find(const struct locale * lang, const struct message_type * mtype) { nrmessage_type *found = NULL; - unsigned int hash = hashstring(mtype->name) % NRT_MAXHASH; + unsigned int hash = mtype->key % NRT_MAXHASH; nrmessage_type *type = nrtypes[hash]; while (type) { if (type->mtype == mtype) { @@ -101,7 +101,7 @@ void nrt_register(const struct message_type *mtype, const struct locale *lang, const char *string, int level, const char *section) { - unsigned int hash = hashstring(mtype->name) % NRT_MAXHASH; + unsigned int hash = mtype->key % NRT_MAXHASH; nrmessage_type *nrt = nrtypes[hash]; while (nrt && (nrt->lang != lang || nrt->mtype != mtype)) { nrt = nrt->next; diff --git a/src/util/rand.c b/src/util/rand.c index f5c8a28bc..441461068 100644 --- a/src/util/rand.c +++ b/src/util/rand.c @@ -36,23 +36,27 @@ int lovar(double xpct_x2) return (rng_int() % n + rng_int() % n) / 1000; } -/* NormalRand aus python, random.py geklaut, dort ist Referenz auf -* den Algorithmus. mu = Mittelwert, sigma = Standardabweichung. -* http://de.wikipedia.org/wiki/Standardabweichung#Diskrete_Gleichverteilung.2C_W.C3.BCrfel -*/ +/* gaussian distribution + * taken from http://c-faq.com/lib/gaussian.html + */ + double normalvariate(double mu, double sigma) { - static const double NV_MAGICCONST = 1.7155277699214135; - double z; - for (;;) { - double u1 = rng_double(); - double u2 = 1.0 - rng_double(); - z = NV_MAGICCONST * (u1 - 0.5) / u2; - if (z * z / 4.0 <= -log(u2)) { - break; - } + static double U, V; + static int phase = 0; + double Z; + + if (phase == 0) { + U = (rng_int() + 1.) / (RNG_RAND_MAX + 2.); + V = rng_int() / (RNG_RAND_MAX + 1.); + Z = sqrt(-2 * log(U)) * sin(2 * PI_D * V); } - return mu + z * sigma; + else { + Z = sqrt(-2 * log(U)) * cos(2 * PI_D * V); + } + phase = 1 - phase; + + return mu + Z *sigma; } int ntimespprob(int n, double p, double mod) @@ -73,7 +77,7 @@ bool chance(double x) { if (x >= 1.0) return true; - return rng_double() < x; + return (1-rng_double()) < x; } typedef struct random_source { diff --git a/src/util/unicode.c b/src/util/unicode.c index 161983ba7..10d43f947 100644 --- a/src/util/unicode.c +++ b/src/util/unicode.c @@ -57,7 +57,7 @@ int unicode_utf8_trim(utf8_t *buf) if (op == buf && iswspace(wc)) { ++result; } - else if (iswprint(wc)) { + else if (wc>255 || !iscntrl(wc)) { if (op != ip) { memmove(op, ip, size); } @@ -67,7 +67,8 @@ int unicode_utf8_trim(utf8_t *buf) lc = op; ts = 0; } - } else { + } + else { ++result; } ip += size; diff --git a/src/util/unicode.test.c b/src/util/unicode.test.c index 7c1c1031a..7cada9da6 100644 --- a/src/util/unicode.test.c +++ b/src/util/unicode.test.c @@ -123,6 +123,17 @@ 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 }; + char name[64]; + strcpy(name, str); + CuAssertIntEquals(tc, 0, unicode_utf8_trim(name)); + CuAssertStrEquals(tc, str, name); +} + CuSuite *get_unicode_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -132,5 +143,6 @@ CuSuite *get_unicode_suite(void) 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); return suite; } diff --git a/tests/run-turn.sh b/tests/run-turn.sh index 9297278c3..fd6eb63ee 100755 --- a/tests/run-turn.sh +++ b/tests/run-turn.sh @@ -28,12 +28,12 @@ while [ ! -d $ROOT/.git ]; do ROOT=`dirname $ROOT` done -set -e cd $ROOT/tests setup cleanup VALGRIND=`which valgrind` SERVER=../Debug/eressea/eressea +set -e if [ -n "$VALGRIND" ]; then SUPP=../share/ubuntu-12_04.supp SERVER="$VALGRIND --track-origins=yes --gen-suppressions=all --suppressions=$SUPP --error-exitcode=1 --leak-check=no $SERVER" @@ -53,6 +53,7 @@ assert_grep_count reports/$CRFILE '^BURG' 1 assert_grep_count reports/$CRFILE '^EINHEIT' 2 assert_grep_count reports/$CRFILE '^GEGENSTAENDE' 2 +assert_grep_count reports/185-heg.cr '185;Runde' 1 assert_grep_count reports/185-heg.cr ';Baeume' 4 assert_grep_count reports/185-heg.cr '"B.ume";type' 4 assert_grep_count reports/185-heg.cr '"Pferde";type' 6 @@ -62,4 +63,4 @@ assert_grep_count reports/185-heg.cr '"neighbour";visibility' 11 assert_grep_count reports/185-6rLo.cr '^EINHEIT' 2 assert_grep_count reports/185-6rLo.cr '^REGION' 13 echo "integration tests: PASS" -#cleanup +cleanup diff --git a/tests/runtests.bat b/tests/runtests.bat index 7a58e5762..b6f70768f 100644 --- a/tests/runtests.bat +++ b/tests/runtests.bat @@ -3,12 +3,12 @@ IF EXIST ..\build-vs10 SET BUILD=..\build-vs10\eressea\Debug IF EXIST ..\build-vs11 SET BUILD=..\build-vs11\eressea\Debug IF EXIST ..\build-vs12 SET BUILD=..\build-vs12\eressea\Debug IF EXIST ..\build-vs14 SET BUILD=..\build-vs14\eressea\Debug -IF EXIST ..\build-vs15 SET BUILD=..\build-vs15\eressea\Debug SET SERVER=%BUILD%\eressea.exe %BUILD%\test_eressea.exe %SERVER% ..\scripts\run-tests.lua %SERVER% -re2 ..\scripts\run-tests-e2.lua %SERVER% -re3 ..\scripts\run-tests-e3.lua +%SERVER% --version PAUSE RMDIR /s /q reports DEL score score.alliances datum turn