diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..9cebe6d6d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,26 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true + +# 4 space indentation +[*.{c,h,lua}] +indent_style = space +indent_size = 4 + +[*.{xml,json}] +charset = utf-8 + +# Tab indentation (no size specified) +[Makefile] +indent_style = tab + +# Matches exact files +[.travis.yml] +indent_style = space +indent_size = 2 diff --git a/conf/e2/config.json b/conf/e2/config.json index fa8566eab..217cfffe9 100644 --- a/conf/e2/config.json +++ b/conf/e2/config.json @@ -11,6 +11,7 @@ "settings": { "game.id": 2, "game.name": "Eressea", + "orders.default": "work", "NewbieImmunity": 8, "modules.wormholes": true, "entertain.base": 0, diff --git a/conf/e2/config.xml b/conf/e2/config.xml index d1eac12d8..5516fd601 100644 --- a/conf/e2/config.xml +++ b/conf/e2/config.xml @@ -67,9 +67,5 @@ ERESSEA 2 BEFEHLE ERESSEA 2 ORDERS - - ARBEITEN - WORK - diff --git a/conf/e2/terrains.json b/conf/e2/terrains.json index d26c656ea..5a8cf0280 100644 --- a/conf/e2/terrains.json +++ b/conf/e2/terrains.json @@ -283,14 +283,6 @@ }, "wall1": { "flags": [ "forbidden", "land" ] - }, - "default": { - "size": 0, - "herbs": [], - "seed": 0, - "road": 0, - "flags": [ "land", "walk", "sail", "fly" ], - "production": {} } } } diff --git a/conf/e3/config.json b/conf/e3/config.json index a1fc05b74..c9a86c8da 100644 --- a/conf/e3/config.json +++ b/conf/e3/config.json @@ -27,6 +27,7 @@ "settings": { "game.id": 3, "game.name": "E3", + "orders.default": "work", "database.gameid": 7, "NewbieImmunity": 4, "modules.astralspace": false, diff --git a/conf/e3/config.xml b/conf/e3/config.xml index 0531f69d2..04af3858d 100644 --- a/conf/e3/config.xml +++ b/conf/e3/config.xml @@ -59,9 +59,5 @@ ERESSEA 3 BEFEHLE ERESSEA 3 ORDERS - - ARBEITEN - WORK - diff --git a/conf/e4/config.json b/conf/e4/config.json index 12da61937..7a73de86f 100644 --- a/conf/e4/config.json +++ b/conf/e4/config.json @@ -27,6 +27,7 @@ "settings": { "game.id": 4, "game.name": "Deveron", + "orders.default": "work", "database.gameid": 7, "NewbieImmunity": 4, "modules.astralspace": false, @@ -49,7 +50,7 @@ "recruit.allow_merge": true, "study.expensivemigrants": true, "study.speedup": 2, - "study.from_use": 0.4, + "study.produceexp": 12, "world.era": 3, "rules.reserve.twophase": true, "rules.owners.force_leave": false, diff --git a/conf/e4/config.xml b/conf/e4/config.xml index ffa1c5df8..2def4f1a8 100644 --- a/conf/e4/config.xml +++ b/conf/e4/config.xml @@ -60,9 +60,5 @@ ERESSEA 4 BEFEHLE ERESSEA 4 ORDERS - - ARBEITEN - WORK - diff --git a/crypto b/crypto index 93dc9200f..913358a8d 160000 --- a/crypto +++ b/crypto @@ -1 +1 @@ -Subproject commit 93dc9200fa4cb6bfa3883b19f6d33fd416ca43da +Subproject commit 913358a8d7d961ffc35b238c744ca6ce823ffdd9 diff --git a/res/adamantium.xml b/res/adamantium.xml index d5cdd4fae..cc1cedadc 100644 --- a/res/adamantium.xml +++ b/res/adamantium.xml @@ -3,7 +3,7 @@ - + @@ -13,7 +13,7 @@ - + @@ -26,7 +26,7 @@ - + diff --git a/res/buildings/castle-2.xml b/res/buildings/castle-2.xml index a54957415..593e9ae5e 100644 --- a/res/buildings/castle-2.xml +++ b/res/buildings/castle-2.xml @@ -3,22 +3,22 @@ - + - + - + - + - + - + diff --git a/res/buildings/castle.xml b/res/buildings/castle.xml index 9a63d7dc8..253f49811 100644 --- a/res/buildings/castle.xml +++ b/res/buildings/castle.xml @@ -2,25 +2,25 @@ - + - + - + - + - + - + - + diff --git a/res/core/armor/chainmail.xml b/res/core/armor/chainmail.xml index 81152f89e..b03221b35 100644 --- a/res/core/armor/chainmail.xml +++ b/res/core/armor/chainmail.xml @@ -1,7 +1,7 @@ - + diff --git a/res/core/armor/laenmail.xml b/res/core/armor/laenmail.xml index bdcf6b7e1..0e1411945 100644 --- a/res/core/armor/laenmail.xml +++ b/res/core/armor/laenmail.xml @@ -1,7 +1,7 @@ - + diff --git a/res/core/armor/laenshield.xml b/res/core/armor/laenshield.xml index f5eb2d783..8a9d6d5c3 100644 --- a/res/core/armor/laenshield.xml +++ b/res/core/armor/laenshield.xml @@ -1,7 +1,7 @@ - + diff --git a/res/core/armor/plate.xml b/res/core/armor/plate.xml index eb10f6359..a22064e90 100644 --- a/res/core/armor/plate.xml +++ b/res/core/armor/plate.xml @@ -2,7 +2,7 @@ - + diff --git a/res/core/armor/rustychainmail.xml b/res/core/armor/rustychainmail.xml index a52b4db7b..b46380634 100644 --- a/res/core/armor/rustychainmail.xml +++ b/res/core/armor/rustychainmail.xml @@ -1,7 +1,7 @@ - + diff --git a/res/core/armor/rustyshield.xml b/res/core/armor/rustyshield.xml index 4c34f7ee1..56a8abe5a 100644 --- a/res/core/armor/rustyshield.xml +++ b/res/core/armor/rustyshield.xml @@ -1,7 +1,7 @@ - + diff --git a/res/core/armor/shield.xml b/res/core/armor/shield.xml index 9a72baf9a..b0ee7de3a 100644 --- a/res/core/armor/shield.xml +++ b/res/core/armor/shield.xml @@ -1,7 +1,7 @@ - + diff --git a/res/core/common/buildings.xml b/res/core/common/buildings.xml index 63ac3e650..423e90deb 100644 --- a/res/core/common/buildings.xml +++ b/res/core/common/buildings.xml @@ -66,7 +66,7 @@ - + @@ -76,7 +76,7 @@ - + @@ -86,7 +86,7 @@ - + @@ -98,7 +98,7 @@ - + @@ -139,7 +139,7 @@ - + @@ -149,7 +149,7 @@ - + @@ -159,7 +159,7 @@ - + diff --git a/res/core/common/construction.xml b/res/core/common/construction.xml index 95dcb6a33..089ddfe1c 100644 --- a/res/core/common/construction.xml +++ b/res/core/common/construction.xml @@ -1,10 +1,10 @@ - + - + diff --git a/res/core/common/items.xml b/res/core/common/items.xml index c8910e695..6afd220ff 100644 --- a/res/core/common/items.xml +++ b/res/core/common/items.xml @@ -143,7 +143,7 @@ - + diff --git a/res/core/common/potions.xml b/res/core/common/potions.xml index f444e5a94..84b1e5f52 100644 --- a/res/core/common/potions.xml +++ b/res/core/common/potions.xml @@ -7,7 +7,7 @@ - + @@ -18,7 +18,7 @@ - + @@ -29,7 +29,7 @@ - + @@ -40,7 +40,7 @@ - + @@ -51,7 +51,7 @@ - + @@ -64,7 +64,7 @@ - + @@ -77,7 +77,7 @@ - + @@ -90,7 +90,7 @@ - + @@ -103,7 +103,7 @@ - + @@ -116,7 +116,7 @@ - + @@ -129,7 +129,7 @@ - + @@ -142,7 +142,7 @@ - + @@ -155,7 +155,7 @@ - + @@ -169,7 +169,7 @@ - + @@ -184,7 +184,7 @@ - + diff --git a/res/core/de/strings.xml b/res/core/de/strings.xml index 5ff71eaca..0b09a972b 100644 --- a/res/core/de/strings.xml +++ b/res/core/de/strings.xml @@ -2906,23 +2906,6 @@ toad - - Alp - nightmare - - - Alps - nightmaress - - - Alps - nightmares - - - Alp - nightmare - - Bergwächter mountainguard @@ -4357,10 +4340,6 @@ Traumdeuten Mind Probe - - Alp - Nightmare - Erschaffe ein Traumauge Create a Visioneye @@ -4485,10 +4464,6 @@ Heimstein Homestone - - Mauern der Ewigkeit - Eternal Walls - Wasserelementar Water Elemental @@ -6424,25 +6399,6 @@ target's faction, skills and possessions will no longer be unknown. - - Der Magier beschwört ein kleines Monster, einen Alp. Dieses bewegt sich - langsam auf sein Opfer zu (das sich an einem beliebigen Ort an der Welt - befinden kann, der Magier oder seine Partei braucht es nicht zu sehen). - Sobald das Opfer erreicht ist, wird es gnadenlos gequält, und nur durch - einen starken Gegenzauber oder den Tod des beschwörenden Magiers kann - das Opfer wieder Frieden finden. Bei der Beschwörung des Alps verliert - der Magier einen kleinen Teil seiner Aura für immer. - The magician spawns a little monster, a nightmare. The nightmare slowly - approaches its victim (which may be at an arbitrary place in eressea, it - is not needed for the magician or his party to see the victim). As soon - as - the victim is reached the nightmare starts to torment it without mercy, - only a powerfull counter spell or the death of the casting magician can - redeem - the victim. When spawning the nightmare the magician loses a small amount - of - his aura forever. - Ein mit diesem Zauber belegtes Drachenauge, welches zum Abendmahle verzehrt wird, erlaubt es dem Benutzer, in die Träume einer anderen @@ -7354,11 +7310,6 @@ fire dragon - - Ein Alp starb, ohne sein Ziel zu erreichen. - An alp died before it reached its target. - - unbewaffnet unarmed @@ -7619,4 +7570,22 @@ + + Groaamm... + + + Tschrrrk... + Tshrrrk... + + + Schhhhh... + Shhhhhh... + + + Roaarrr... + + + Chrrr... + + diff --git a/res/core/messages.xml b/res/core/messages.xml index 9a207ad5f..4669ab8b1 100644 --- a/res/core/messages.xml +++ b/res/core/messages.xml @@ -65,13 +65,6 @@ $unit($owner) bittet $unit($unit), $building($building) zu verlassen. $unit($owner) asks $unit($unit) to leave $building($building). - - - - - Ein Alp starb in $region($region), ohne sein Ziel zu erreichen. - An alp died in $region($region) before reaching its target. - @@ -1104,13 +1097,6 @@ "Plötzlich löst sich $building($building) in kleine Traumwolken auf." "$building($building) suddenly dissolves into small pink clouds." - - - - - "Für das Gebäude $building($building) konnte die ganze Woche kein Unterhalt bezahlt werden." - "Upkeep for $building($building) could not be paid all week." - @@ -1277,11 +1263,6 @@ "$unit($unit) stumbles upon $localize($location) while exploring the region. Closer inspection reveals a torn old book titled '$localize($book)'. The expansion of knowledge is tremendous." - - "Ein Alp hat sein Opfer gefunden und springt auf den Rücken von $unit($target)!" - "An evil spirit has found its victim and mounts the back of $unit($target)!" - - @@ -1469,16 +1450,6 @@ "$unit($mage) calls forth a terrible torment over the enemy. The magical rain makes all iron rusty." - - - - - - - "$unit($mage) beschwört den Alp $unit($alp) für $unit($target)." - "$unit($mage) summons the alp $unit($alp) for $unit($target)." - - @@ -7109,13 +7080,6 @@ "Der Unterhalt von $building($building) konnte nicht gezahlt werden, das Gebäude war diese Woche nicht funktionstüchtig." "The upkeep for $building($building) was not paid, the building was not operational this week." - - - - - "Der Unterhalt von $building($building) konnte nur verspätet gezahlt werden, das Gebäude war diese Woche nicht funktionstüchtig." - "The upkeep for $building($building) was paid late, the building was not operational this week." - @@ -7315,8 +7279,8 @@ - "$unit($unit) konnte durch einen Heiltrank überleben." - "$unit($unit) was saved by a healing potion." + "Eine Person von $unit($unit) konnte durch einen Heiltrank überleben." + "A fighter of $unit($unit) was saved by a healing potion." @@ -8463,4 +8427,15 @@ "$unit($unit) in $region($region): '$order($command)' - Heroes cannot recruit." + + + + + + + + "$unit($dragon): \"$localize($growl) $if($eq($number,1), "Ich rieche", "Wir riechen") etwas in $region($target)\"." + "$unit($dragon): \"$localize($growl) $if($eq($number,1), "I smell", "We smell") something in $region($target)\"." + + diff --git a/res/core/resources/cart.xml b/res/core/resources/cart.xml index af3860c9c..17dbdcb41 100644 --- a/res/core/resources/cart.xml +++ b/res/core/resources/cart.xml @@ -1,7 +1,7 @@ - + diff --git a/res/core/resources/horse.xml b/res/core/resources/horse.xml index abf6d856d..53699c696 100644 --- a/res/core/resources/horse.xml +++ b/res/core/resources/horse.xml @@ -1,7 +1,7 @@ - + diff --git a/res/core/resources/iron.xml b/res/core/resources/iron.xml index 94dbc6390..7bc4cb428 100644 --- a/res/core/resources/iron.xml +++ b/res/core/resources/iron.xml @@ -1,7 +1,7 @@ - + diff --git a/res/core/resources/laen.xml b/res/core/resources/laen.xml index efb13c5ed..571026a0a 100644 --- a/res/core/resources/laen.xml +++ b/res/core/resources/laen.xml @@ -1,7 +1,7 @@ - + diff --git a/res/core/resources/log.xml b/res/core/resources/log.xml index c017682fc..012fe9a20 100644 --- a/res/core/resources/log.xml +++ b/res/core/resources/log.xml @@ -1,7 +1,7 @@ - + diff --git a/res/core/resources/mallorn.xml b/res/core/resources/mallorn.xml index e5c01506b..2d301758e 100644 --- a/res/core/resources/mallorn.xml +++ b/res/core/resources/mallorn.xml @@ -1,7 +1,7 @@ - + diff --git a/res/core/resources/mallornseed.xml b/res/core/resources/mallornseed.xml index 1c7c1a800..7a5a0310f 100644 --- a/res/core/resources/mallornseed.xml +++ b/res/core/resources/mallornseed.xml @@ -1,6 +1,6 @@ - + diff --git a/res/core/resources/seed.xml b/res/core/resources/seed.xml index 2193fb8c8..99f5f5804 100644 --- a/res/core/resources/seed.xml +++ b/res/core/resources/seed.xml @@ -1,6 +1,6 @@ - + diff --git a/res/core/resources/stone.xml b/res/core/resources/stone.xml index eed2fd2df..c227dcc5c 100644 --- a/res/core/resources/stone.xml +++ b/res/core/resources/stone.xml @@ -1,7 +1,7 @@ - + diff --git a/res/core/ships.xml b/res/core/ships.xml index 6c6821be4..fc87bc3d3 100644 --- a/res/core/ships.xml +++ b/res/core/ships.xml @@ -1,39 +1,34 @@ - - + - - + - - + - - + - @@ -44,11 +39,10 @@ - + - @@ -59,13 +53,12 @@ - + - @@ -76,7 +69,7 @@ - + diff --git a/res/core/weapons/axe.xml b/res/core/weapons/axe.xml index 7ee77b21e..80a872ad7 100644 --- a/res/core/weapons/axe.xml +++ b/res/core/weapons/axe.xml @@ -2,7 +2,7 @@ - + diff --git a/res/core/weapons/bow.xml b/res/core/weapons/bow.xml index c64392966..4fb9b2000 100644 --- a/res/core/weapons/bow.xml +++ b/res/core/weapons/bow.xml @@ -1,7 +1,7 @@ - + diff --git a/res/core/weapons/catapult.xml b/res/core/weapons/catapult.xml index 08dc83457..e05c5a025 100644 --- a/res/core/weapons/catapult.xml +++ b/res/core/weapons/catapult.xml @@ -1,7 +1,7 @@ - + diff --git a/res/core/weapons/crossbow.xml b/res/core/weapons/crossbow.xml index 318628b81..c7bf4715d 100644 --- a/res/core/weapons/crossbow.xml +++ b/res/core/weapons/crossbow.xml @@ -1,7 +1,7 @@ - + diff --git a/res/core/weapons/greatbow.xml b/res/core/weapons/greatbow.xml index 542bd41a8..10b270e55 100644 --- a/res/core/weapons/greatbow.xml +++ b/res/core/weapons/greatbow.xml @@ -2,7 +2,7 @@ - + diff --git a/res/core/weapons/greatsword.xml b/res/core/weapons/greatsword.xml index 481ff680c..cd48b8296 100644 --- a/res/core/weapons/greatsword.xml +++ b/res/core/weapons/greatsword.xml @@ -2,7 +2,7 @@ - + diff --git a/res/core/weapons/halberd.xml b/res/core/weapons/halberd.xml index bf3fb4af7..5025f4793 100644 --- a/res/core/weapons/halberd.xml +++ b/res/core/weapons/halberd.xml @@ -2,7 +2,7 @@ - + diff --git a/res/core/weapons/laensword.xml b/res/core/weapons/laensword.xml index 31ec8b9e6..3053d5f1b 100644 --- a/res/core/weapons/laensword.xml +++ b/res/core/weapons/laensword.xml @@ -1,7 +1,7 @@ - + diff --git a/res/core/weapons/lance.xml b/res/core/weapons/lance.xml index 1271c48f9..abbb7f31f 100644 --- a/res/core/weapons/lance.xml +++ b/res/core/weapons/lance.xml @@ -2,7 +2,7 @@ - + diff --git a/res/core/weapons/mallornbow.xml b/res/core/weapons/mallornbow.xml index f61ea8e97..abdcc4810 100644 --- a/res/core/weapons/mallornbow.xml +++ b/res/core/weapons/mallornbow.xml @@ -1,7 +1,7 @@ - + diff --git a/res/core/weapons/mallorncrossbow.xml b/res/core/weapons/mallorncrossbow.xml index fb7be26c0..385720d8c 100644 --- a/res/core/weapons/mallorncrossbow.xml +++ b/res/core/weapons/mallorncrossbow.xml @@ -1,7 +1,7 @@ - + diff --git a/res/core/weapons/mallornlance.xml b/res/core/weapons/mallornlance.xml index 88973f3e0..0186143e4 100644 --- a/res/core/weapons/mallornlance.xml +++ b/res/core/weapons/mallornlance.xml @@ -2,7 +2,7 @@ - + diff --git a/res/core/weapons/mallornspear.xml b/res/core/weapons/mallornspear.xml index 8c3782e4c..66d41a943 100644 --- a/res/core/weapons/mallornspear.xml +++ b/res/core/weapons/mallornspear.xml @@ -1,7 +1,7 @@ - + diff --git a/res/core/weapons/rep_crossbow.xml b/res/core/weapons/rep_crossbow.xml index 1e550a190..66e3f9fc2 100644 --- a/res/core/weapons/rep_crossbow.xml +++ b/res/core/weapons/rep_crossbow.xml @@ -2,7 +2,7 @@ - + diff --git a/res/core/weapons/rustyaxe.xml b/res/core/weapons/rustyaxe.xml index 5399de62d..2ff19570c 100644 --- a/res/core/weapons/rustyaxe.xml +++ b/res/core/weapons/rustyaxe.xml @@ -2,7 +2,7 @@ - + diff --git a/res/core/weapons/rustygreatsword.xml b/res/core/weapons/rustygreatsword.xml index ac1385b11..7f21cce17 100644 --- a/res/core/weapons/rustygreatsword.xml +++ b/res/core/weapons/rustygreatsword.xml @@ -1,7 +1,7 @@ - + diff --git a/res/core/weapons/rustyhalberd.xml b/res/core/weapons/rustyhalberd.xml index 4f237b989..c1b1f69a9 100644 --- a/res/core/weapons/rustyhalberd.xml +++ b/res/core/weapons/rustyhalberd.xml @@ -2,7 +2,7 @@ - + diff --git a/res/core/weapons/rustysword.xml b/res/core/weapons/rustysword.xml index f73dcd6e2..2a76c109b 100644 --- a/res/core/weapons/rustysword.xml +++ b/res/core/weapons/rustysword.xml @@ -1,7 +1,7 @@ - + diff --git a/res/core/weapons/spear.xml b/res/core/weapons/spear.xml index c6479707a..32a79b9a2 100644 --- a/res/core/weapons/spear.xml +++ b/res/core/weapons/spear.xml @@ -1,7 +1,7 @@ - + diff --git a/res/core/weapons/sword.xml b/res/core/weapons/sword.xml index 0d3830e2e..4bd2ebdb1 100644 --- a/res/core/weapons/sword.xml +++ b/res/core/weapons/sword.xml @@ -1,7 +1,7 @@ - + diff --git a/res/e3a/armor/chainmail.xml b/res/e3a/armor/chainmail.xml index 39743d754..509df07e6 100644 --- a/res/e3a/armor/chainmail.xml +++ b/res/e3a/armor/chainmail.xml @@ -1,7 +1,7 @@ - + diff --git a/res/e3a/armor/laenmail.xml b/res/e3a/armor/laenmail.xml index d3b1dd533..f03add574 100644 --- a/res/e3a/armor/laenmail.xml +++ b/res/e3a/armor/laenmail.xml @@ -1,7 +1,7 @@ - + diff --git a/res/e3a/armor/laenshield.xml b/res/e3a/armor/laenshield.xml index 1ce6216b2..8d003bccc 100644 --- a/res/e3a/armor/laenshield.xml +++ b/res/e3a/armor/laenshield.xml @@ -1,7 +1,7 @@ - + diff --git a/res/e3a/armor/plate.xml b/res/e3a/armor/plate.xml index 550e83173..29cabbf1a 100644 --- a/res/e3a/armor/plate.xml +++ b/res/e3a/armor/plate.xml @@ -2,7 +2,7 @@ - + diff --git a/res/e3a/armor/rustychainmail.xml b/res/e3a/armor/rustychainmail.xml index ac2628c62..361564c76 100644 --- a/res/e3a/armor/rustychainmail.xml +++ b/res/e3a/armor/rustychainmail.xml @@ -1,7 +1,7 @@ - + diff --git a/res/e3a/armor/rustyshield.xml b/res/e3a/armor/rustyshield.xml index 2becae180..d9d8a54ef 100644 --- a/res/e3a/armor/rustyshield.xml +++ b/res/e3a/armor/rustyshield.xml @@ -1,7 +1,7 @@ - + diff --git a/res/e3a/armor/scale.xml b/res/e3a/armor/scale.xml index 809d08bca..7039b4008 100644 --- a/res/e3a/armor/scale.xml +++ b/res/e3a/armor/scale.xml @@ -2,7 +2,7 @@ - + diff --git a/res/e3a/armor/shield.xml b/res/e3a/armor/shield.xml index 9b4ad9174..5770711e2 100644 --- a/res/e3a/armor/shield.xml +++ b/res/e3a/armor/shield.xml @@ -1,7 +1,7 @@ - + diff --git a/res/e3a/armor/towershield.xml b/res/e3a/armor/towershield.xml index 5a7edcabe..e80c03523 100644 --- a/res/e3a/armor/towershield.xml +++ b/res/e3a/armor/towershield.xml @@ -2,7 +2,7 @@ - + diff --git a/res/e3a/buildings.xml b/res/e3a/buildings.xml index 231596dc6..8ca30d27d 100644 --- a/res/e3a/buildings.xml +++ b/res/e3a/buildings.xml @@ -3,24 +3,24 @@ - + - + + + + + + + - - - - - - - + diff --git a/res/e3a/items.xml b/res/e3a/items.xml index 706bd0d67..7d7e6b286 100644 --- a/res/e3a/items.xml +++ b/res/e3a/items.xml @@ -71,7 +71,7 @@ - + diff --git a/res/e3a/races.xml b/res/e3a/races.xml index 83b77dd3a..aeafc4218 100644 --- a/res/e3a/races.xml +++ b/res/e3a/races.xml @@ -672,11 +672,6 @@ - - - - - diff --git a/res/e3a/resources/iron.xml b/res/e3a/resources/iron.xml index 3856c045d..b245da358 100644 --- a/res/e3a/resources/iron.xml +++ b/res/e3a/resources/iron.xml @@ -1,7 +1,7 @@ - + diff --git a/res/e3a/resources/stone.xml b/res/e3a/resources/stone.xml index 854003c86..746b79c21 100644 --- a/res/e3a/resources/stone.xml +++ b/res/e3a/resources/stone.xml @@ -1,7 +1,7 @@ - + diff --git a/res/e3a/ships.xml b/res/e3a/ships.xml index 1174eda8e..291900a3b 100644 --- a/res/e3a/ships.xml +++ b/res/e3a/ships.xml @@ -1,7 +1,6 @@ - @@ -11,24 +10,22 @@ - + - - + - @@ -38,13 +35,12 @@ - + - @@ -54,14 +50,13 @@ - + - @@ -71,17 +66,16 @@ - + - - + @@ -89,19 +83,17 @@ - - + - - + @@ -109,10 +101,9 @@ - - + @@ -120,10 +111,9 @@ - - + @@ -133,10 +123,9 @@ - - + @@ -145,10 +134,9 @@ - - + diff --git a/res/e3a/strings.xml b/res/e3a/strings.xml index d0647a6d1..054cabfaa 100644 --- a/res/e3a/strings.xml +++ b/res/e3a/strings.xml @@ -207,153 +207,69 @@ - - 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. + + 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 peasant's harvest will be extraordinary good. - (OBS: this spell has been changed and needs a new translation). + 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. - Für dieses Ernteritual sendet der Druide seine arkane Energie entlang - der astralen Kraftlinien der gesamten Umgebung, um selbst weit entfernte - Naturgeister zu erreichen. Die Beschwörung dieser Naturgeister ist eine - hohe Kunst, die höchste Konzentration und vor allem viel Erfahrung - erfordert. Die Bauern werden nach und nach von den besseren Ernten - profitieren und ihre Ersparnisse steigern können. - This ritual increases the output of the local farms. - Peasants in the region produce an extra silverpiece. The stronger the - druid's spell is, the longer the effect will last. - (OBS: this spell has been changed and needs a new translation). + Für dieses Ernteritual sendet der Druide seine arkane Energie entlang der astralen Kraftlinien der gesamten Umgebung, um selbst weit entfernte Naturgeister zu erreichen. Die Beschwörung dieser Naturgeister ist eine hohe Kunst, die höchste Konzentration und vor allem viel Erfahrung erfordert. Die Bauern werden nach und nach von den besseren Ernten profitieren und ihre Ersparnisse steigern können. + This ritual not only increases the output of the local farms. Activating the astral ley lines, the druid is capable of activating even the nature spirits far away. Peasants' harvest in the affected regions will be extraordinarily good for a few weeks. - In den dunkleren Gassen gibt es sie, - die Flüche und Verhexungen auf Bestellung. Aber - auch Gegenzauber hat der Jünger des Draigs - natürlich im Angebot. Ob nun der Sohn des - Nachbarn in einen Liebesbann gezogen werden soll - oder die Nebenbuhlerin Pickel und Warzen - bekommen soll, niemand gibt gerne zu, zu solchen - Mitteln gegriffen zu haben. Für diese - Dienstleistung streicht der Magier 25 Silber pro - Stufe ein. - In the dark alleys you can find those - who sell curses and hexes on demand - but you - can buy the apropriate counterspells from the - followers of Draig as well. May it be a love - spell for the son of a neighbour or a wart in - the face of a rival. For offering these - services, the sorcerer charges 25 silver pieces. - per level. + In den dunkleren Gassen gibt es sie, die Flüche und Verhexungen auf Bestellung. Aber auch Gegenzauber hat der Jünger des Draigs natürlich im Angebot. Ob nun der Sohn des Nachbarn in einen Liebesbann gezogen werden soll oder die Nebenbuhlerin Pickel und Warzen bekommen soll: niemand gibt gerne zu, zu solchen Mitteln gegriffen zu haben. Für diese Dienstleistung streicht der Magier 25 Silber pro Stufe ein. + In the dark alleys you can find those who sell curses and hexes on demand -- but you can buy the apropriate counterspells from the followers of Draig as well. May it be a love spell for the son of a neighbour or a wart in the face of a rival. For offering these services, the sorcerer charges 25 silver pieces per level. - Niemand kann so gut die Träume deuten - wie ein Magier des Illaun. Auch die Kunst der - Wahrsagerei, des Kartenlegens und des Handlesens - sind ihm geläufig. Dafür zahlen ihm die Bauern - 25 Silber pro Stufe. - No one can read dreams as well as the - mages of Illaun. Furthermore, they are also - familiar with all other common means of - foretelling the future like crystal balls, tarot - cards or palms. A mentalist can earn 25 silver - pieces per level and week for offering these - services to peasants. + Niemand kann so gut die Träume deuten wie ein Magier des Illaun. Auch die Kunst der Wahrsagerei, des Kartenlegens und des Handlesens sind ihm geläufig. Dafür zahlen ihm die Bauern 25 Silber pro Stufe. + No one can read dreams as well as the mages of Illaun. Furthermore, they are also familiar with all other common means of foretelling the future like crystal balls, tarot cards or palms. A mentalist can earn 25 silver pieces per level and week for offering these services to peasants. - Wenn einem der Alchemist nicht weiterhelfen kann, geht man zu dem - gelehrten Tybiedmagier. Seine Tränke und Tinkturen helfen gegen - alles, was man sonst nicht bekommen kann. Ob nun die kryptische - Formel unter dem Holzschuh des untreuen Ehemannes wirklich geholfen - hat - nun, der des Lesens nicht mächtige Bauer wird es nie wissen. - Dem Magier hilft es auf jeden Fall... beim Füllen seines - Geldbeutels. 25 Silber pro Stufe lassen sich so in einer Woche - verdienen. - If the local alchemist could not help you, you should visit a - scholar of Tybied. His potions and tinctures may help when nothing - else does. If the cryptic formula under the wooden shoes of the - unfaithful husband really helped? - well, the peasant, who isn't - capable of reading, will never know. At least it helped the magician... - to fill his purse. In one week he can earn 25 silver per level that - way. + Wenn einem der Alchemist nicht weiterhelfen kann, geht man zu dem gelehrten Tybiedmagier. Seine Tränke und Tinkturen helfen gegen alles, was man sonst nicht bekommen kann. Ob nun die kryptische Formel unter dem Holzschuh des untreuen Ehemannes wirklich geholfen hat -- nun, der des Lesens nicht mächtige Bauer wird es nie wissen. Dem Magier hilft es auf jeden Fall... beim Füllen seines Geldbeutels. 25 Silber pro Stufe lassen sich so in einer Woche verdienen. + If the local alchemist could not help you, you should visit a scholar of Tybied. His potions and tinctures may help when nothing else does. If the cryptic formula under the wooden shoes of the unfaithful husband really helped? - Well, the peasant, who isn't capable of reading, will never know. At least it helped the magician... to fill his purse. In one week he can earn 25 silver per level that way. - Cerddormagier sind _die_ Gaukler unter - den Magiern, sie lieben es das Volk zu unterhalten und - im Mittelpunkt zu stehen. Schon Anfänger lernen die - kleinen Kunststücke und magischen Tricks, mit denen man - das Volk locken und verführen kann, den Geldbeutel ganz - weit zu öffnen, und am Ende der Woche wird der Gaukler - 25 Silber pro Stufe verdient haben. - The mages of Cerddor truly are the - bards of the wizards; they love to use their sorcery to - entertain the crowds and to be the center of attention. - Even the apprentices study those little magic tricks, - which attract and fascinate the people and thus ensnare - them into leaving a few coins or more for the artist. By - the end of the week, the bard will have earned 25 silver - per level. + Cerddormagier sind _die_ Gaukler unter den Magiern; sie lieben es, das Volk zu unterhalten und im Mittelpunkt zu stehen. Schon Anfänger lernen die kleinen Kunststücke und magischen Tricks, mit denen man das Volk locken und verführen kann, den Geldbeutel ganz weit zu öffnen, und am Ende der Woche wird der Gaukler 25 Silber pro Stufe verdient haben. + The mages of Cerddor truly are the bards of the wizards; they love to use their sorcery to entertain the crowds and to be the center of attention. Even the apprentices study those little magic tricks, which attract and fascinate the people and thus ensnare them into leaving a few coins or more for the artist. By the end of the week, the bard will have earned 25 silver per level. - Die Fähigkeiten der Gwyrrd-Magier in - der Viehzucht und Heilung sind bei den Bauern sehr - begehrt. Gerade auf Märkten sind ihre Dienste häufig sehr - gefragt. Manch einer mag auch sein Talent dazu nutzen, - ein Tier für einen besseren Preis zu verkaufen. Pro - Stufe kann der Magier so 25 Silber verdienen. - The abilities of the mages of Gwyrrd - concerning the breeding and healing of cattle are highly - appreciated among the peasants. Especially at the - markets, their services are demanded frequently. Some of - them also use their talents to sell an animal at a - higher price. A magician can earn 25 silver pieces per level - in this way. + Die Fähigkeiten der Gwyrrd-Magier in der Viehzucht und Heilung sind bei den Bauern sehr begehrt. Gerade auf Märkten sind ihre Dienste häufig sehr gefragt. Manch einer mag auch sein Talent dazu nutzen, ein Tier für einen besseren Preis zu verkaufen. Pro Stufe kann der Magier so 25 Silber verdienen. + The abilities of the mages of Gwyrrd concerning the breeding and healing of cattle are highly appreciated among the peasants. Especially at the markets their services are demanded frequently. Some of them also use their talents to sell an animal at a higher price. A magician can earn 25 silver pieces per level in this way. - Time is one of the first mysteries every magician tries to solve. If he succeeds, he can focus all his energies on his magical studies as the ways between dormatory, library and magician tower now pass much faster. To keep his heart in tune the magician uses a special self made tey. Some mages even share it with up to 10 people - Die Zeit ist eines der ersten Geheimnisse, die jeder Magier zu erkunden versucht. Gelingt ihm dies, kann er alle seine Energie auf das Studium der Magie verwenden, die Wege zwischen Dormitorium, Bibliothek und Magierturm schafft er nun viel schneller. Um sein Herz im Takt zu halten verwendet er einen speziellen selbstgemachten Tee. Manche Magier teilen diesen Tee mit bis zu 10 Personen. + Time is one of the first mysteries every magician tries to solve. If they succeed, they can focus all their energies on their magical studies as the ways between dormatory, library and magician tower now pass much faster. To keep their heart in tune the magicians use a special self-made tea. Some mages even share it with up to 10 people + Die Zeit ist eines der ersten Geheimnisse, die jeder Magier zu erkunden versucht. Gelingt ihm dies, kann er alle seine Energie auf das Studium der Magie verwenden; die Wege zwischen Dormitorium, Bibliothek und Magierturm schafft er nun viel schneller. Um sein Herz im Takt zu halten, verwendet er einen speziellen selbstgemachten Tee. Manche Magier teilen diesen Tee mit bis zu 10 Personen. - One of the most strange spells enables the magician to withdraw a little life energy from his surroundings to produce the so called water of life. The juice of some selected herbs is used to conserve the energies of life. As the magician is protected by his magical abilities, he is the only one who can touch the juice without taking damage. This is necessary because the juice must be carried around from place to place to avoid damaging one region by taking too much energy at once. - In einem der seltsamsten Zauber kann der Magier seiner Umgebung ein klein wenig der Lebensenergie entziehen um das sogenannte Wasser des Lebens herstellen. Als Basis dient ihm hierbei der Saft aus einigen ausgesuchten Kräutern, welcher die Lebensenergie speichern kann. Da der Magier aufgrund seiner magischen Fähigkeiten immun ist, ist er der einzige, der den Saft berühren kann, ohne Schaden zu nehmen. Sodenn muß er diesen eine Woche lang von Ort zu Ort tragen, damit er sich mit Lebensenergie vollsaugt, ohne einem einzelnen Ort soviel zu entfernen, daß er Schaden nimmt. + One of the most strange spells enables the magician to withdraw a little life energy from their surroundings to produce the so called water of life. The juice of some selected herbs is used to conserve the energies of life. As the magician is protected by his magical abilities, he is the only one who can touch the juice without taking damage. This is necessary because the juice must be carried around from place to place to avoid damaging one region by taking too much energy at once. + In einem der seltsamsten Zauber kann der Magier seiner Umgebung ein klein wenig der Lebensenergie entziehen um das sogenannte Wasser des Lebens herstellen. Als Basis dient ihm hierbei der Saft aus einigen ausgesuchten Kräutern, welcher die Lebensenergie speichern kann. Da der Magier aufgrund seiner magischen Fähigkeiten immun ist, ist er der einzige, der den Saft berühren kann, ohne Schaden zu nehmen. Sodann muss er diesen eine Woche lang von Ort zu Ort tragen, damit er sich mit Lebensenergie vollsaugt, ohne einem einzelnen Ort soviel zu entfernen, dass er Schaden nimmt. - Busybeer is another application for the knowledge about time a magician accumulates. Even though manual labour is only seldom done by mages, they still show interest in increasing the efficiency of their minions that do this work for them. Mornac the wise was the first to discover how to produce a potion that would enable 10 minions to do the work of 20, thus freeing the other 10 for different experiments. - Ein weiteres Anwendungsgebiet des Wissens über die Zeit welches ein Magier ansammelt stellt der Schaffenstrunk dar. Auch wenn körperliche Arbeiten eher selten von Magiern ausgeführt werden, so haben diese doch ein Interesse daran, die Effizienz ihrer Untergebenen bei solchen Arbeiten zu steigern. Mornac der Weise war der erste, der entdeckte, daß man einen Trunk herstellen kann, durch den 10 Untergebene die arbeit von 20 erledigen können, wodurch 10 für andere Experimente weiterverwendet werden konnten. + Busybeer is another application for the knowledge about time a magician accumulates. Even though manual labour is only seldom done by mages, they still show interest in increasing the efficiency of their minions that do this work for them. Mornac the Wise was the first to discover how to produce a potion that would enable 10 minions to do the work of 20, thus freeing the other 10 for different experiments. + Ein weiteres Anwendungsgebiet des Wissens über die Zeit, welches ein Magier ansammelt, stellt der Schaffenstrunk dar. Auch wenn körperliche Arbeiten eher selten von Magiern ausgeführt werden, so haben diese doch ein Interesse daran, die Effizienz ihrer Untergebenen bei solchen Arbeiten zu steigern. Mornac der Weise war der erste, der entdeckte, dass man einen Trunk herstellen kann, durch den 10 Untergebene die Arbeit von 20 erledigen, wodurch der Rest für andere Experimente eingesetzt werden kann. - In the aftermath of battle it is vital to heal your own troops. This can be done by a healer as well as by a magician. In contrast to a healer, the magician can fullfill his treatment already before the battle by binding his magical powers into a potent salve. This salve can be stored and only needs to be applied to the wounds after the battle. - Nach einem harten Kampf sollte man sich heilen lassen. Diese Möglichkeit bietet der Magier ebenso wie der Heiler. Im Gegensatz zum Heiler ist der Magier jedoch in der Lage, seine Behandlung bereits vor dem Kampf durchzuführen, indem er seine Heilkräfte in eine magische Salbe bindet, welche gelagert werden kann und nach dem Kampf nur aufgetragen werden muß. + In the aftermath of battle it is vital to heal your own troops. This can be done by a healer as well as by a magician. In contrast to a healer, the magician can fullfill this treatment already before the battle by binding magical powers into a potent salve. This salve can be stored and only needs to be applied to the wounds after the battle. + Nach einem harten Kampf sollte man sich heilen lassen. Diese Möglichkeit bietet der Magier ebenso wie der Heiler. Im Gegensatz zum Heiler ist der Magier jedoch in der Lage, seine Behandlung bereits vor dem Kampf durchzuführen, indem er seine Heilkräfte in eine magische Salbe bindet, welche gelagert werden kann und nach dem Kampf nur aufgetragen werden muss. - One of the most dangerous and best guarded secrets of all mages is the knowledge about the power of death. Even though most of them would not openly admit it, and it is at least partially forbidden in most countries, each of them studies death sooner or later. When they do, they quickly find out that there is another plane of existance, the home of the demons. Only blood can quelch the thirst of those, when they switch from their home to our world. But experienced mages will find out that the demons bloodwine can be deluted when apropiate herbs are included, making it enough for 100 instead of 10 demons. As the demons may not know about that, the magician has to secretly sacrifice one of his freed minions. - Zu den gefährlichsten und geheimsten Wissen der Magier zählt das Wissen über die Macht des Todes. Auch wenn die meisten es nicht zugeben, so fasziniert dieses Thema jeden Magier. Früher oder später beschäftigen sich alle mit diesem, teilweise verbotenen, Gebiet. Sodann werden sie feststellen, das es noch eine weitere Ebene der Existenz gibt, in der die Dämonen beheimatet sind. Nur Blut allein vermag den Hunger dieser Wesen zu befriedigen, wenn sie ihre Ebene verlassen und unsere betreten. Erfahrene Magier werden jedoch feststellen, dass man den Blutwein, den die Dämonen zu sich nehmen strecken kann, so daß davon 100 anstatt nur 10 Dämonen satt werden. Da die Dämonen davon jedoch nichts wissen dürfen, muß der Magier selbst klammheimlich einen seiner urplötzlich verfügbar gewordenen Untergebenen opfern. + One of the most dangerous and best guarded secrets of all mages is the knowledge about the power of death. Even though most of them would not openly admit it, and it is at least partially forbidden in most countries, each of them studies death sooner or later. When they do, they quickly find out that there is another plane of existance: the home of the demons. Only blood can quelch the thirst of those, when they switch from their home to our world. But experienced mages will find out that the demons bloodwine can be deluted when appropiate herbs are included, making it enough for 100 instead of 10 demons. As the demons may not know about that, the magician has to secretly sacrifice one of his freed minions. + Zu dem gefährlichsten und geheimsten Wissen der Magier zählt das Wissen über die Macht des Todes. Auch wenn die meisten es nicht zugeben, so fasziniert dieses Thema jeden Magier. Früher oder später beschäftigen sich alle mit diesem teilweise verbotenen Gebiet. Sodann werden sie feststellen, dass es noch eine weitere Ebene der Existenz gibt, in der die Dämonen beheimatet sind. Nur Blut allein vermag den Hunger dieser Wesen zu befriedigen, wenn sie ihre Ebene verlassen und unsere betreten. Erfahrene Magier werden jedoch feststellen, dass man den Blutwein, den die Dämonen zu sich nehmen, strecken kann, so dass davon 100 anstatt nur 10 Dämonen satt werden. Da die Dämonen davon jedoch nichts wissen dürfen, muss der Magier selbst klammheimlich einen seiner urplötzlich verfügbar gewordenen Untergebenen opfern. - Even though mages live in strict cellibate they know a lot about creating certain longings. Peasants keep asking them for this love potion or that. But the penality for bewitching a peasant is death, so the mages offer their services only to farmers for their breeding stock. In an elaborate ritual, which only serves to hide the simplicity of the procedure, the magician draws the fluids from certains plants. while doing so, he calls upon the spirits of fertility which of course only listen when he talkes to them. Now the farmer knows that any attempt to draw the fluids himself will only result in a useless waste of resorces. Finally, the magician hands the vial with the fluids to the farmer, who pours it into his horses drinking water. - Obwohl für Magier das Gebiet der Liebe Tabu ist und sie im strengen Zölibat leben, haben sie ein großes Wissen darüber, wie man gewisse Bedürfnisse weckt, weshalb sie immer wieder von Dorfbewohnerinnen und Dorfbewohnern nach entsprechenden Zaubern gefragt werden. Da die Verzauberung eines Bewohners jedoch streng verboten ist, bieten sie ihre Dienste nur für die Züchter an. In einem Aufwendigen Ritual, welches jedoch nur dazu dient zu verschleiern, wie einfach dies eigentlich ist, vermischt der Magier vor den Augen des Züchters einige Pflanzensäfte. Dabei ruft er die Geister an die dem Pferdezüchter das Glück bescheren sollen, um klarzumachen, das diese nur mit ihm sprechen und jeglicher Versuch des Züchters, selbst die Kräuter zu mischen nur eine unbrauchbare Pampe produzieren würde. Anschließend überreicht der Magier dem Züchter eine Phiole, die dieser in die Tränke seiner Pferde entleeren muß. + Even though mages live in strict celibacy, they know a lot about creating certain longings. Peasants keep asking them for this love potion or that. But the penality for bewitching a peasant is death, so the mages offer their services only to farmers for their breeding stock. In an elaborate ritual, which only serves to hide the simplicity of the procedure, the magician draws the fluids from certains plants. While doing so he calls upon the spirits of fertility, which of course only listen when he talkes to them. Now the farmer knows that any attempt to draw the fluids himself will only result in a useless waste of resources. Finally, the magician hands the vial with the fluids to the farmer, who pours it into his horses' drinking water. + Obwohl für Magier das Gebiet der Liebe tabu ist und sie im strengen Zölibat leben, haben sie ein großes Wissen darüber, wie man gewisse Bedürfnisse weckt, weshalb sie immer wieder von Dorfbewohnerinnen und Dorfbewohnern nach entsprechenden Zaubern gefragt werden. Da die Verzauberung eines Bewohners jedoch streng verboten ist, bieten sie ihre Dienste nur für die Züchter an. In einem aufwendigen Ritual, welches jedoch nur dazu dient zu verschleiern, wie einfach dies eigentlich ist, vermischt der Magier vor den Augen des Züchters einige Pflanzensäfte. Dabei ruft er die Geister an, die dem Pferdezüchter das Glück bescheren sollen, um klarzumachen, das diese nur mit ihm sprechen und jeglicher Versuch des Züchters, selbst die Kräuter zu mischen, nur eine unbrauchbare Pampe produzieren würde. Anschließend überreicht der Magier dem Züchter eine Phiole, die dieser in die Tränke seiner Pferde entleeren muss. - Just like with the knowledge about death, the peasants feel uncomfortable with the knowledge about monsters. A few warriors though, who have already faced these creatures in combat, foud that the monsters blood had en invigourating effect on them. There is talk about some warriors, who bathed in the blood of the slain monsters to take up their strenght. But this effect ends soon, and only occurs with fresh blood. As no one has time to quickly slay a wyrm before attacking his neighbors, a way had to be found to make the effect last longer. After lots of experiments that cost the life of lots of good warriors who had to constantly bring in fresh dragon blood, Manasouf the black finally found a way. Originally a closely guarded secret, the recipe is now knows in all lands. First, the hardened dragon blood needs to be melted in hot tin. After that, the magician binds the spirit of the dragon to its blood once again. It can not fnd eternal rest until the last bit of blood has been used. - Ebenso wie das Wissen über den Tod ist das Wissen über gewisse Monster bei der abergläubigen Bevölkerung nicht gerne gesehen. Einige wenige Krieger jedoch, die diesen Kreaturen schon mal im Kampf gegenüberstanden haben entdeckt, daß deren Blut eine belebende Wirkung auf sie hatte. So solle es schon Kriger gegeben haben, die im Blut der erschlagenen Monster badeten, um deren Stärke in sich aufzunehmen. Diese Wirkung verfliegt jedoch rasch, und wirkt nur bei frischen Blut. Da niemand vor dem Kampf gegen seinen Nachbarn die Zeit hat, schnell noch einen Wyrm zu erschlagen musste ein Weg gefunden werden, die Wirkung haltbar zu machen. Manasouf dem schwarzen gelang dies nach zahlreichen Experimenten, die das Leben vieler guter Männer kosteten, welche ständig neues Drachenblut für seine Versuche beschaffen mussten. Ursprünglich ein streng gehütetes Geheimnis, ist das Rezept inzwischen im ganzen Land bekannt. Zunächst muß geronnene Drachenblut muß in einem Tiegel wieder verflüssigt werden. Anschließend wird der Geist des erschlagenen Drachen in der Geisterebene wieder an sein Blut gebunden, und kann solange nicht in frieden ruhen, bis das letzte bisschen seines Blutes verbraucht wurde. + Just like with the knowledge about death, the peasants feel uncomfortable with the knowledge about monsters. A few warriors though, who have already faced these creatures in combat, found that the monsters blood had an invigourating effect on them. There is talk about some warriors who bathed in the blood of the slain monsters to take up their strength. But this effect ends soon, and only occurs with fresh blood. As no one has time to quickly slay a wyrm before attacking their neighbors, a way had to be found to make the effect last longer. After lots of experiments that cost the life of lots of good warriors who had to constantly bring in fresh dragon blood, Manasouf the black finally found a way. Originally a closely guarded secret, the recipe is now known in all lands. First, the hardened dragon blood needs to be melted in hot tin. After that, the magician binds the spirit of the dragon to its blood once again. It can not find eternal rest until the last bit of blood has been used. + Ebenso wie das Wissen über den Tod ist das Wissen über gewisse Monster bei der abergläubigen Bevölkerung nicht gerne gesehen. Einige wenige Krieger jedoch, die diesen Kreaturen schon mal im Kampf gegenüberstanden, haben entdeckt, dass deren Blut eine belebende Wirkung auf sie hatte. So soll es schon Krieger gegeben haben, die im Blut der erschlagenen Monster badeten, um deren Stärke in sich aufzunehmen. Diese Wirkung verfliegt jedoch rasch und wirkt nur bei frischen Blut. Da niemand vor dem Kampf gegen seinen Nachbarn die Zeit hat, schnell noch einen Wyrm zu erschlagen, musste ein Weg gefunden werden, die Wirkung haltbar zu machen. Manasouf dem Schwarzen gelang dies nach zahlreichen Experimenten, die das Leben vieler guter Männer kosteten, welche ständig neues Drachenblut für seine Versuche beschaffen mussten. Ursprünglich ein streng gehütetes Geheimnis ist das Rezept inzwischen im ganzen Land bekannt. Zunächst muss geronnenes Drachenblut in heißem Zinn verflüssigt werden. Anschließend wird der Geist des erschlagenen Drachen in der Geisterebene wieder an sein Blut gebunden und kann so lange nicht in Frieden ruhen, bis das letzte bisschen seines Blutes verbraucht wurde. - Some mages research deth's secrets until they can bring the dead back to life. But those who are brought back are often only shadows of ther former self and turn against their erstwhile friends. But those mages that study life and its iteraction with death find a possibility, to bring the deceased back as their original selves. A drawback is that this is only possible in the very first minutes after the death. As even mages can not be everywhere at the same time, a way had to be found to give this ability to helpers. All healers who tried to learn this from the mages failed, though, until one of those healers was backstabbingly killed. In the moment of his death he used the knowledge gained and was able to have his murderer executed the following day. The potion he designed has to be blessed by a magician before usage at any given time. This potion gives 4 people (or 1 person 4 times) a 50% chance to survive an otherwise deadly wound. It is used automatically by the victom. - Manche Magier erforschen den Tod, bis sie verstorbene wieder ins Leben zurück bringen können. Diese sind jedoch meist bösartig und nur noch Schatten ihres früheren selbst. Diejenigen jedoch, die sich intensiv mit dem Leben und seiner Kombination mit dem Tod beschäftigen finden eine Möglichkeit, verstorbene in ihrer wahren Gestallt zurück zu rufen. Dies ist allerdings nur wenige Minuten nach dem Tod möglich. Da selbst Magier nicht überall gleichzeitig sein können, musste ein Weg gefunden werden, diese Fähigkeit auf andere zu übertragen. Alle Versuche, dies feldschern beizubringen scheiterten jedoch, bis einer dieser Felschner von einem Widersacher hinterrücks ermordet wurde. Im Moment seines Todes wandte er sein erworbenes Wissen an und konnte tags darauf den Ãœbeltäter wegen Mordes hinrichten lassen. Der von ihm entwickelte magische Trank muß jedoch von einem der Magie des Lebens kundigen gesegnet werden, um seine volle Wirkung zu entfalten. Ein solcher Trank gibt vier Männern (oder einem Mann vier mal) im Kampf eine Chance von 50%, sonst tödliche Wunden zu überleben. Der Trank wird von ihnen automatisch bei Verletzung angewandt. + Some mages research death's secrets until they can bring the dead back to life. But those who are brought back are often only shadows of ther former self and turn against their erstwhile friends. But those mages that study life and its iteraction with death find a possibility to bring the deceased back as their original selves. A drawback is that this is only possible in the very first minutes after death. As even mages can not be everywhere at the same time, a way had to be found to give this ability to helpers. All healers who tried to learn this from the mages failed, though, until one of those healers was backstabbingly killed. In the moment of his death he used the knowledge gained and was able to have his murderer executed the following day. The potion he designed has to be blessed by a magician before usage at any given time. This potion gives four people (or one person four times) a 50% chance to survive an otherwise deadly wound. It is used automatically by the victom. + Manche Magier erforschen den Tod, bis sie Verstorbene wieder ins Leben zurück bringen können. Diese sind jedoch meist bösartig und nur noch Schatten ihres früheren Selbst. Diejenigen jedoch, die sich intensiv mit dem Leben und seiner Kombination mit dem Tod beschäftigen, finden eine Möglichkeit, Verstorbene in ihrer wahren Gestalt zurück zu rufen. Dies ist allerdings nur wenige Minuten nach dem Tod möglich. Da selbst Magier nicht überall gleichzeitig sein können, musste ein Weg gefunden werden, diese Fähigkeit auf andere zu übertragen. Alle Versuche, dies Feldschern beizubringen, scheiterten jedoch, bis einer dieser Feldscher von einem Widersacher hinterrücks ermordet wurde. Im Moment seines Todes wandte er sein erworbenes Wissen an und konnte tags darauf den Ãœbeltäter wegen Mordes hinrichten lassen. Der von ihm entwickelte magische Trank muss jedoch von einem der Magie des Lebens Kundigen gesegnet werden, um seine volle Wirkung zu entfalten. Ein solcher Trank gibt vier Männern (oder einem Mann viermal) im Kampf eine Chance von 50%, sonst tödliche Wunden zu überleben. Der Trank wird von ihnen automatisch bei Verletzung angewandt. @@ -365,13 +281,8 @@ - Einem erfahrenen Magier wird - irgendwann auf seinen Wanderungen ein - ungewöhnliches Exemplar einer Gattung begegnen, - welches sich dem Magier anschließen wird. - During their travel, seasoned - magicians will occasionally befriend an extraordinary - creature of an unusual species that will join them. + Einem erfahrenen Magier wird irgendwann auf seinen Wanderungen ein ungewöhnliches Exemplar einer Gattung begegnen, welches sich dem Magier anschließen wird. + During their travel, seasoned magicians will occasionally befriend an extraordinary creature of an unusual species that will join them. diff --git a/res/e3a/weapons/crossbow.xml b/res/e3a/weapons/crossbow.xml index 57535a070..7b2ed743c 100644 --- a/res/e3a/weapons/crossbow.xml +++ b/res/e3a/weapons/crossbow.xml @@ -1,7 +1,7 @@ - + diff --git a/res/e3a/weapons/greatbow.xml b/res/e3a/weapons/greatbow.xml index ba91f54de..7f08427c8 100644 --- a/res/e3a/weapons/greatbow.xml +++ b/res/e3a/weapons/greatbow.xml @@ -6,7 +6,7 @@ - + diff --git a/res/e3a/weapons/greatsword.xml b/res/e3a/weapons/greatsword.xml index 5fc1c9042..5a199321d 100644 --- a/res/e3a/weapons/greatsword.xml +++ b/res/e3a/weapons/greatsword.xml @@ -4,7 +4,7 @@ --> - + diff --git a/res/e3a/weapons/halberd.xml b/res/e3a/weapons/halberd.xml index edb933701..2c11b62fc 100644 --- a/res/e3a/weapons/halberd.xml +++ b/res/e3a/weapons/halberd.xml @@ -5,7 +5,7 @@ - + diff --git a/res/e3a/weapons/laensword.xml b/res/e3a/weapons/laensword.xml index 13ebbf03e..50f824e8c 100644 --- a/res/e3a/weapons/laensword.xml +++ b/res/e3a/weapons/laensword.xml @@ -4,7 +4,7 @@ --> - + diff --git a/res/e3a/weapons/mallorncrossbow.xml b/res/e3a/weapons/mallorncrossbow.xml index 920ae553f..86991bc10 100644 --- a/res/e3a/weapons/mallorncrossbow.xml +++ b/res/e3a/weapons/mallorncrossbow.xml @@ -1,7 +1,7 @@ - + diff --git a/res/e3a/weapons/mallornlance.xml b/res/e3a/weapons/mallornlance.xml index 6355f5780..1fcad08c8 100644 --- a/res/e3a/weapons/mallornlance.xml +++ b/res/e3a/weapons/mallornlance.xml @@ -2,7 +2,7 @@ - + diff --git a/res/e3a/weapons/rustygreatsword.xml b/res/e3a/weapons/rustygreatsword.xml index 818fac553..607f16a6a 100644 --- a/res/e3a/weapons/rustygreatsword.xml +++ b/res/e3a/weapons/rustygreatsword.xml @@ -4,7 +4,7 @@ --> - + diff --git a/res/e3a/weapons/rustyhalberd.xml b/res/e3a/weapons/rustyhalberd.xml index bab849b49..ed25d5db7 100644 --- a/res/e3a/weapons/rustyhalberd.xml +++ b/res/e3a/weapons/rustyhalberd.xml @@ -5,7 +5,7 @@ - + diff --git a/res/eressea/races.xml b/res/eressea/races.xml index f87505357..e91d2f6f6 100644 --- a/res/eressea/races.xml +++ b/res/eressea/races.xml @@ -671,11 +671,6 @@ - - - - - diff --git a/res/eressea/spellbooks/gray.xml b/res/eressea/spellbooks/gray.xml index 21e1609a0..a7f718f2a 100644 --- a/res/eressea/spellbooks/gray.xml +++ b/res/eressea/spellbooks/gray.xml @@ -142,7 +142,6 @@ - diff --git a/res/eressea/spellbooks/illaun.xml b/res/eressea/spellbooks/illaun.xml index 37f3e73b2..ff99a0a81 100644 --- a/res/eressea/spellbooks/illaun.xml +++ b/res/eressea/spellbooks/illaun.xml @@ -8,7 +8,6 @@ - diff --git a/res/eressea/spells.xml b/res/eressea/spells.xml index f7d81abfb..5af11913e 100644 --- a/res/eressea/spells.xml +++ b/res/eressea/spells.xml @@ -188,13 +188,6 @@ - diff --git a/res/eressea/strings.xml b/res/eressea/strings.xml index 6b6cf8903..b7db0eb4b 100644 --- a/res/eressea/strings.xml +++ b/res/eressea/strings.xml @@ -284,10 +284,6 @@ getting it after the second turn, please make one of your units give the order OPTION COMPUTER. - - ARBEITEN - WORK - Tempel temple diff --git a/res/ships/boat.xml b/res/ships/boat.xml index 28d48392f..551fea702 100644 --- a/res/ships/boat.xml +++ b/res/ships/boat.xml @@ -1,6 +1,5 @@ - @@ -11,7 +10,7 @@ - + diff --git a/s/cmake-init b/s/cmake-init index 05bae5540..35378d6a5 100755 --- a/s/cmake-init +++ b/s/cmake-init @@ -16,6 +16,7 @@ MACHINE=`uname -m` [ -z "$CC" ] && [ ! -z `which cc` ] && CC="cc" BIN_DIR="$ROOT/build-$MACHINE-$CC-$BUILD" mkdir -p $BIN_DIR +rm -f $BUILD ln -sf $BIN_DIR $BUILD MACHINE=$(gcc -dumpmachine) diff --git a/scripts/eressea/autoseed.lua b/scripts/eressea/autoseed.lua index e5cd2e478..e948705c0 100644 --- a/scripts/eressea/autoseed.lua +++ b/scripts/eressea/autoseed.lua @@ -1,8 +1,8 @@ local autoseed = {} -- minimum required resources in the 7-hex neighborhood: -local peasants = 20000 -local trees = 1000 +local peasants = 10000 +local trees = 800 -- number of starters per region: local per_region = 2 @@ -23,8 +23,12 @@ local function select_regions(regions, peasants, trees) local sel = {} for r in regions do if not r.plane and r.terrain~="ocean" and not r.units() then - if score(r, "peasant") >= peasants and score(r, "tree") >= trees then - table.insert(sel, r) + sp = score(r, "peasant") + st = score(r, "tree") + if sp >= peasants then + if st >= trees then + table.insert(sel, r) + end end end end @@ -47,8 +51,11 @@ local function read_players() end local function seed(r, email, race, lang) + assert(r) local f = faction.create(email, race, lang) + assert(f) local u = unit.create(f, r) + assert(u) equip_unit(u, "new_faction") equip_unit(u, "first_unit") equip_unit(u, "first_" .. race, 7) -- disable old callbacks @@ -69,31 +76,38 @@ end function autoseed.init() -- local newbs = {} - local num_seeded = per_region + local num_seeded = 0 local start = nil eressea.log.info('autoseed new players') players = read_players() + if players then + print('autoseed ' .. #players .. ' new players') + end if players and #players >= per_region then local sel eressea.log.info(#players .. ' new players') sel = select_regions(regions(), peasants, trees) - for _, p in ipairs(players) do - if num_seeded == per_region then - while not start or start.units() do - local index = 1 + (rng_int() % #sel) - start = sel[index] + if #sel == 0 then + eressea.log.error("autoseed could not select regions for new factions") + else + for _, p in ipairs(players) do + if num_seeded == per_region then + local index = rng_int() % #sel + while not start do + start = sel[index + 1] + index = (index + 1) % #sel + end + num_seeded = 0 + end + local dupe = get_faction_by_email(p.email) + if dupe then + eressea.log.warning("seed: duplicate email " .. p.email .. " already used by " .. tostring(dupe)) + else + print("new faction ".. p.email .. " starts in ".. tostring(start)) + local f = seed(start, p.email, p.race or "human", p.lang or "de") + num_seeded = num_seeded + 1 end - num_seeded = 0 - end - local dupe = get_faction_by_email(p.email) - if dupe then - eressea.log.warning("seed: duplicate email " .. p.email .. " already used by " .. tostring(dupe)) - else - local f = seed(start, p.email, p.race or "human", p.lang or "de") - num_seeded = num_seeded + 1 - print("new faction ".. tostring(f) .. " starts in ".. tostring(start)) - -- table.insert(newbs, f) end end end diff --git a/scripts/eressea/e3/init.lua b/scripts/eressea/e3/init.lua index 9acda1093..e376eaadc 100644 --- a/scripts/eressea/e3/init.lua +++ b/scripts/eressea/e3/init.lua @@ -6,7 +6,7 @@ eressea.log.debug("rules for game E3") return { require('eressea'), require('eressea.xmasitems'), - require('eressea.markets'), + -- require('eressea.markets'), require('eressea.frost'), require('eressea.ents') } diff --git a/scripts/eressea/e4/init.lua b/scripts/eressea/e4/init.lua index e5e617c55..32c0b8498 100644 --- a/scripts/eressea/e4/init.lua +++ b/scripts/eressea/e4/init.lua @@ -5,7 +5,7 @@ eressea.log.debug("rules for game E4") return { require('eressea'), - require('eressea.markets'), + -- require('eressea.markets'), require('eressea.frost'), require('eressea.ents') } diff --git a/scripts/eressea/xmasitems.lua b/scripts/eressea/xmasitems.lua index 4f8a20767..b31691d72 100644 --- a/scripts/eressea/xmasitems.lua +++ b/scripts/eressea/xmasitems.lua @@ -15,6 +15,23 @@ local function error_message(msg, u, ord) return -1 end + +local function usepotion_message(u, type) + msg = message.create("usepotion") + msg:set_unit("unit", u) + msg:set_resource("potion", type) + return msg +end + +function use_stardust(u, amount) + local p = u.region:get_resource("peasant") + p = math.ceil(1.5 * p) + u.region:set_resource("peasant", p) + local msg = usepotion_message(u, "stardust") + msg:send_region(u.region) + return 1 +end + function use_snowglobe(u, amount, token, ord) local transform = { ocean = "glacier", @@ -59,9 +76,7 @@ function use_xmastree(u, amount) local trees = u.region:get_resource("tree") u.region:set_key("xm06", true) u.region:set_resource("tree", 10+trees) - local msg = message.create("usepotion") - msg:set_unit("unit", u) - msg:set_resource("potion", "xmastree") + local msg = usepotion_message(u, "xmastree") msg:send_region(u.region) return amount end diff --git a/scripts/kill-planes.lua b/scripts/kill-planes.lua new file mode 100644 index 000000000..3919f3fda --- /dev/null +++ b/scripts/kill-planes.lua @@ -0,0 +1,28 @@ +path = 'scripts' +if config.install then + path = config.install .. '/' .. path + package.path = package.path .. ';' .. config.install .. '/lunit/?.lua' + --needed to find lunit if not run from eressea root. Needs right [lua] install setting in eressea.ini (point to eressea root from the start folder) +end +package.path = package.path .. ';' .. path .. '/?.lua;' .. path .. '/?/init.lua' + +config.rules = 'e2' + +require 'eressea' +require 'eressea.xmlconf' +require 'eressea.path' + +eressea.read_game(get_turn() .. '.dat') +ids = {2081501646, 1967748303, 1137, 2000, 1456894557, 1580742069, 1143084084, 285224813, 604912520, 296884068, 50} +p=plane.create(50, -7280, -4494, 83, 83, "Regatta") + +for k,v in ipairs(ids) do + p = plane.get(v) + print(v, p) + p:erase() +end +eressea.write_game(get_turn() .. '.new') +eressea.free_game() +eressea.read_game(get_turn() .. '.new') +write_reports() +eressea.write_game(get_turn() .. '.fix') diff --git a/scripts/newplayer.lua b/scripts/newplayer.lua index 9e536595b..c946248fb 100644 --- a/scripts/newplayer.lua +++ b/scripts/newplayer.lua @@ -1,32 +1,13 @@ -dofile("config.lua") -p = require("populate") - -local function read_players() --- return {{ email = "noreply@mailinator.com", race = "dwarf", lang = "de" }} - local players = {} - local input = io.open("newfactions", "r") - while input do - local str = input:read("*line") - if str==nil then break end - local email, race, lang = str:match("([^ ]*) ([^ ]*) ([^ ]*)") - if string.char(string.byte(email, 1))~='#' then - table.insert(players, { race = race, lang = lang, email = email }) - end - end - return players +local path = 'scripts' +if config.install then + path = config.install .. '/' .. path end +package.path = package.path .. ';' .. path .. '/?.lua;' .. path .. '/?/init.lua' +require 'eressea' +require 'eressea.xmlconf' -- read xml data -local function seed(r, email, race, lang) - local f = faction.create(email, race, lang) - local u = unit.create(f, r) - equip_unit(u, "new_faction") - equip_unit(u, "first_unit") - equip_unit(u, "first_" .. race, 7) -- disable old callbacks - unit.create(f, r, 5):set_skill("mining", 30) - unit.create(f, r, 5):set_skill("quarrying", 30) - f:set_origin(r) - return f -end +require 'config' +auto = require 'eressea.autoseed' local function dump_selection(sel) local best = { score = 0, r = nil } @@ -42,54 +23,9 @@ local function dump_selection(sel) return best end -players = read_players() -local peasants = 20000 -local trees = 1000 -local turn = get_turn() -local sel -if #players > 0 then - eressea.read_game(("%d.dat"):format(turn)) - sel = p.select(regions(), peasants, trees) - if #sel > 0 then - local best = dump_selection(sel) - print("finest region, " .. best.score .. " points: " .. tostring(best.r)) - end -end -math.randomseed(os.time()) - -local newbs = {} -local per_region = 2 -local num_seeded = 2 -local start = nil -for _, p in ipairs(players) do - if num_seeded == per_region then - while not start or start.units() do - local index = math.random(#sel) - start = sel[index] - end - num_seeded = 0 - end - local dupe = false - for f in factions() do - if f.email==p.email then - print("seed: duplicate email " .. p.email .. " already used by " .. tostring(f)) - dupe = true - break - end - end - if not dupe then - num_seeded = num_seeded + 1 - f = seed(start, p.email, p.race or "human", p.lang or "de") - print("new faction ".. tostring(f) .. " starts in ".. tostring(start)) - table.insert(newbs, f) - end -end - -if #newbs > 0 then - init_reports() - for _, f in ipairs(newbs) do - write_report(f) - end - eressea.write_game(("%d.dat.new"):format(turn)) -end - +print("read game") +eressea.read_game(get_turn() .. ".dat") +print("auto-seed") +auto.init() +print("editor") +gmtool.editor() diff --git a/scripts/run-turn.lua b/scripts/run-turn.lua index fc5eebbbf..7ee9165ca 100644 --- a/scripts/run-turn.lua +++ b/scripts/run-turn.lua @@ -3,7 +3,6 @@ function nmr_check(maxnmrs) if nmrs >= maxnmrs then eressea.log.error("Shit. More than " .. maxnmrs .. " factions with 1 NMR (" .. nmrs .. ")") write_summary() - eressea.write_game("aborted.dat") return -1 end print (nmrs .. " Factions with 1 NMR") diff --git a/scripts/tests/e2/e2features.lua b/scripts/tests/e2/e2features.lua index 8505bfd39..efed50ea1 100644 --- a/scripts/tests/e2/e2features.lua +++ b/scripts/tests/e2/e2features.lua @@ -2,6 +2,29 @@ require "lunit" module("tests.e2.e2features", package.seeall, lunit.testcase ) +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 u = unit.create(f, r) + size = 30 + u.number = 20 + u:set_skill("building", 3) + u:add_item("money", size*250) + u:add_item("stone", size*5) + u:add_item("log", size*5) + u:clear_orders() + u:add_order("MACHE HAFEN") + process_orders() + assert_not_nil(u.building) + assert_equal("harbour", u.building.type) + assert_equal(20, u.building.size) + process_orders() + assert_equal(25, u.building.size) + process_orders() + assert_equal(25, u.building.size) +end + local function one_unit(r, f) local u = unit.create(f, r, 1) u:add_item("money", u.number * 100) @@ -71,25 +94,6 @@ function test_rename() assert_equal(u:get_item("ao_healing"), 1) end -function DISABLE_test_alp() - local r = region.create(0,0, "plain") - local f = faction.create("noreply@eressea.de", "human", "de") - local u = unit.create(f, r, 1) - local u2 = unit.create(f, r, 1) - u.race = "elf" - u:set_skill("magic", 10) - u:add_item("money", 3010) - u.magic = "illaun" - u.aura = 200 - u.ship = s1 - u:add_spell("summon_alp") - u:clear_orders() - u:add_order("ZAUBERE 'Alp' " .. itoa36(u2.id)) - process_orders() - print(get_turn(), f) - write_reports() -end - function test_unit_limit_is_1500() local r = region.create(0,0, "plain") local f = faction.create("noreply@eressea.de", "human", "de") diff --git a/scripts/tests/e2/movement.lua b/scripts/tests/e2/movement.lua index 7e20e41ca..706ed9ad2 100644 --- a/scripts/tests/e2/movement.lua +++ b/scripts/tests/e2/movement.lua @@ -8,6 +8,31 @@ function setup() eressea.settings.set("NewbieImmunity", "0") end + function test_piracy() + 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 u1 = unit.create(f, r2, 1) + local u2 = unit.create(f2, r3, 1) + + u1.ship = ship.create(r2, "longboat") + u2.ship = ship.create(r3, "longboat") + u1:set_skill("sailing", 10) + u2:set_skill("sailing", 10) + + u1:clear_orders() + u1:add_order("PIRATERIE") + u2:clear_orders() + u2:add_order("NACH o") + + process_orders() + +-- write_reports() + assert_equal(r2, u1.region) -- should pass, but fails!!! +end + function test_dolphin_on_land() local r1 = region.create(0, 0, "plain") local r2 = region.create(1, 0, "plain") diff --git a/scripts/tests/e3/rules.lua b/scripts/tests/e3/rules.lua index 323103e73..2cba83f21 100644 --- a/scripts/tests/e3/rules.lua +++ b/scripts/tests/e3/rules.lua @@ -4,6 +4,14 @@ module("tests.e3.e3features", package.seeall, lunit.testcase) local settings +-- use the C implementation in market.c, because the Lua +-- module is wrong (https://bugs.eressea.de/view.php?id=2225) +local function process_markets() + -- markets = require("eressea.markets") + -- markets.update() + eressea.process.markets() +end + local function set_rule(key, value) if value==nil then eressea.settings.set(key, settings[key]) @@ -403,8 +411,7 @@ function test_market_regions() -- if i am the only trader around, i should be getting all the herbs from all 7 regions local r, u, b, herbnames, luxurynames, herbtable, luxurytable = market_fixture() - - eressea.process.markets() + process_markets() test_items(u, herbtable, 10) test_items(u, luxurytable, 5) @@ -420,9 +427,8 @@ function test_multiple_markets() b2.working = true reset_items(u2) u2.building = b2 - - eressea.process.markets() + process_markets() for _, i in pairs(luxurytable) do assert_equal(5, u1:get_item(i)+u2:get_item(i), "not enough " .. i ) end @@ -432,12 +438,11 @@ function test_multiple_markets() assert_equal(5, u1:get_item('silk')) -- uncontested end - function test_market() local r = region.create(0, 0, "plain") local f1 = faction.create("market2@eressea.de", "human", "de") local u1 = unit.create(f1, r, 1) - + local b = building.create(r, "market") eressea.settings.set("rules.peasants.growth", "0") @@ -469,7 +474,7 @@ function test_market() end reset_items() b.size = 1 - eressea.process.markets() + process_markets() assert_equal(0, u1:get_item("h0")) b.size = 10 @@ -477,38 +482,38 @@ function test_market() reset_items() r:set_resource("peasant", 2100) - eressea.process.markets() + process_markets() assert_equal(5, u1:get_item("h0")) assert_equal(3, u1:get_item("balm")) reset_items() r:set_resource("peasant", 1049) - eressea.process.markets() + process_markets() assert_equal(2, u1:get_item("h0")) assert_equal(1, u1:get_item("balm")) reset_items() r:set_resource("peasant", 550) - eressea.process.markets() + process_markets() assert_equal(2, u1:get_item("h0")) assert_equal(1, u1:get_item("balm")) reset_items() r:set_resource("peasant", 549) - eressea.process.markets() + process_markets() assert_equal(1, u1:get_item("h0")) assert_equal(1, u1:get_item("balm")) reset_items() r:set_resource("peasant", 50) - eressea.process.markets() + process_markets() assert_equal(1, u1:get_item("h0")) assert_equal(1, u1:get_item("balm")) reset_items() r:set_resource("peasant", 49) - eressea.process.markets() + process_markets() assert_equal(0, u1:get_item("h0")) r:set_resource("peasant", 1050) diff --git a/scripts/tests/xmas.lua b/scripts/tests/xmas.lua index 7b4490d7a..e1cd4753b 100644 --- a/scripts/tests/xmas.lua +++ b/scripts/tests/xmas.lua @@ -82,3 +82,18 @@ function test_xmastree() r = use_tree("plain") assert_equal(10, r:get_resource("tree")) end + +function test_stardust() + -- fix random peasant changes: + 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 u = unit.create(f, r, 5) + u:add_item("stardust", 1) + u:clear_orders() + u:add_order("BENUTZEN 1 Sternenstaub") + process_orders() + assert_equal(15, r:get_resource("peasant")) + assert_equal(0, u:get_item('stardust')) +end diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d083ef3b1..fb03ebbe7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -83,6 +83,7 @@ set (ERESSEA_SRC spells.c battle.c alchemy.c + academy.c upkeep.c vortex.c names.c diff --git a/src/triggers/removecurse.h b/src/academy.c similarity index 50% rename from src/triggers/removecurse.h rename to src/academy.c index 406132083..f6b70873e 100644 --- a/src/triggers/removecurse.h +++ b/src/academy.c @@ -16,25 +16,29 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. **/ -#ifndef REMOVECURSE_H -#define REMOVECURSE_H -#ifdef __cplusplus -extern "C" { -#endif +#include +#include +#include +#include +#include +#include +#include "academy.h" +#include "study.h" - /* all types we use are defined here to reduce dependencies */ - struct trigger_type; - struct trigger; - - struct unit; - struct curse; - - extern struct trigger_type tt_removecurse; - - extern struct trigger *trigger_removecurse(struct curse *c, - struct unit *target); - -#ifdef __cplusplus +void academy_teaching_bonus(struct unit *u, skill_t sk, int academy) { + if (academy && sk != NOSKILL) { + learn_skill(u, sk, academy / STUDYDAYS); + } +} + +bool academy_can_teach(unit *teacher, unit *student, skill_t sk) { + const struct building_type *btype = bt_find("academy"); + if (active_building(teacher, btype) && active_building(student, btype)) { + int j = study_cost(student, sk); + j = _max(50, j * 2); + /* kann Einheit das zahlen? */ + return get_pooled(student, get_resourcetype(R_SILVER), GET_DEFAULT, j) >= j; + /* sonst nehmen sie nicht am Unterricht teil */ + } + return false; } -#endif -#endif diff --git a/src/academy.h b/src/academy.h new file mode 100644 index 000000000..f6af93748 --- /dev/null +++ b/src/academy.h @@ -0,0 +1,16 @@ +#ifndef H_ACADEMY +#define H_ACADEMY + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + struct unit; + void academy_teaching_bonus(struct unit *u, skill_t sk, int academy); + bool academy_can_teach(struct unit *teacher, struct unit *student, skill_t sk); +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/alchemy.c b/src/alchemy.c index 3d37af6a7..5e369a3e1 100644 --- a/src/alchemy.c +++ b/src/alchemy.c @@ -21,6 +21,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "alchemy.h" #include "move.h" #include "skill.h" +#include "study.h" #include #include @@ -33,6 +34,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* util includes */ #include +#include #include #include #include @@ -301,8 +303,9 @@ a_writeeffect(const attrib * a, const void *owner, struct storage *store) WRITE_INT(store, edata->value); } -static int a_readeffect(attrib * a, void *owner, struct storage *store) +static int a_readeffect(attrib * a, void *owner, struct gamedata *data) { + struct storage *store = data->store; int power; const resource_type *rtype; effect_data *edata = (effect_data *)a->data.v; @@ -315,6 +318,10 @@ static int a_readeffect(attrib * a, void *owner, struct storage *store) if (rtype == NULL || rtype->ptype == NULL || power <= 0) { return AT_READ_FAIL; } + if (rtype->ptype==oldpotiontype[P_HEAL]) { + // healing potions used to have long-term effects + return AT_READ_FAIL; + } edata->type = rtype->ptype; edata->value = power; return AT_READ_OK; diff --git a/src/attributes/CMakeLists.txt b/src/attributes/CMakeLists.txt index 954f55443..1419f9e9a 100644 --- a/src/attributes/CMakeLists.txt +++ b/src/attributes/CMakeLists.txt @@ -1,6 +1,7 @@ PROJECT(attributes C) SET(_TEST_FILES stealth.test.c +key.test.c otherfaction.test.c ) diff --git a/src/attributes/attributes.c b/src/attributes/attributes.c index afe899b0f..770ba7524 100644 --- a/src/attributes/attributes.c +++ b/src/attributes/attributes.c @@ -57,6 +57,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* util includes */ #include #include +#include #include @@ -64,12 +65,12 @@ attrib_type at_unitdissolve = { "unitdissolve", NULL, NULL, NULL, a_writechars, a_readchars }; -static int read_ext(attrib * a, void *owner, struct storage *store) +static int read_ext(attrib * a, void *owner, gamedata *data) { int len; - READ_INT(store, &len); - store->api->r_bin(store->handle, NULL, (size_t)len); + READ_INT(data->store, &len); + data->store->api->r_bin(data->store->handle, NULL, (size_t)len); return AT_READ_OK; } @@ -125,6 +126,7 @@ void register_attributes(void) at_register(&at_raceprefix); at_register(&at_iceberg); at_register(&at_key); + at_register(&at_keys); at_register(&at_follow); at_register(&at_targetregion); at_register(&at_orcification); diff --git a/src/attributes/dict.c b/src/attributes/dict.c index 7ce81d6a3..fa65d1f3d 100644 --- a/src/attributes/dict.c +++ b/src/attributes/dict.c @@ -31,6 +31,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* util includes */ #include +#include #include #include @@ -95,63 +96,64 @@ dict_write(const attrib * a, const void *owner, struct storage *store) } } -static int dict_read(attrib * a, void *owner, struct storage *store) +static int dict_read(attrib * a, void *owner, gamedata *data) { + storage *store = data->store; char name[NAMESIZE]; - dict_data *data = (dict_data *)a->data.v; + dict_data *dd = (dict_data *)a->data.v; int result, n; float flt; READ_STR(store, name, sizeof(name)); - data->name = _strdup(name); + dd->name = _strdup(name); READ_INT(store, &n); - data->type = (dict_type)n; - switch (data->type) { + dd->type = (dict_type)n; + switch (dd->type) { case TINTEGER: - READ_INT(store, &data->data.i); + READ_INT(store, &dd->data.i); break; case TREAL: READ_FLT(store, &flt); if ((int)flt == flt) { - data->type = TINTEGER; - data->data.i = (int)flt; + dd->type = TINTEGER; + dd->data.i = (int)flt; } else { - data->data.real = flt; + dd->data.real = flt; } break; case TSTRING: READ_STR(store, name, sizeof(name)); - data->data.str = _strdup(name); + dd->data.str = _strdup(name); break; case TBUILDING: result = - read_reference(&data->data.b, store, read_building_reference, - resolve_building); - if (result == 0 && !data->data.b) { + read_reference(&dd->data.b, data, read_building_reference, + resolve_building); + if (result == 0 && !dd->data.b) { return AT_READ_FAIL; } break; case TUNIT: result = - read_reference(&data->data.u, store, read_unit_reference, resolve_unit); - if (result == 0 && !data->data.u) { + read_reference(&dd->data.u, data, read_unit_reference, resolve_unit); + if (result == 0 && !dd->data.u) { return AT_READ_FAIL; } break; case TFACTION: result = - read_reference(&data->data.f, store, read_faction_reference, + read_reference(&dd->data.f, data, read_faction_reference, resolve_faction); - if (result == 0 && !data->data.f) { + if (result == 0 && !dd->data.f) { return AT_READ_FAIL; } break; case TREGION: result = - read_reference(&data->data.r, store, read_region_reference, - RESOLVE_REGION(global.data_version)); - if (result == 0 && !data->data.r) { + read_reference(&dd->data.r, data, read_region_reference, + RESOLVE_REGION(data->version)); + if (result == 0 && !dd->data.r) { return AT_READ_FAIL; } break; diff --git a/src/attributes/follow.c b/src/attributes/follow.c index 1c3f7908e..d68fcaebd 100644 --- a/src/attributes/follow.c +++ b/src/attributes/follow.c @@ -23,13 +23,14 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include +#include #include #include -static int read_follow(attrib * a, void *owner, struct storage *store) +static int read_follow(attrib * a, void *owner, gamedata *data) { - read_unit_reference(store); /* skip it */ + read_unit_reference(data); /* skip it */ return AT_READ_FAIL; } diff --git a/src/attributes/hate.c b/src/attributes/hate.c index 84768944a..3d9a2fb0a 100644 --- a/src/attributes/hate.c +++ b/src/attributes/hate.c @@ -24,6 +24,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include +#include #include #include @@ -43,10 +44,10 @@ write_hate(const attrib * a, const void *owner, struct storage *store) write_unit_reference((unit *)a->data.v, store); } -static int read_hate(attrib * a, void *owner, struct storage *store) +static int read_hate(attrib * a, void *owner, gamedata *data) { - int result = - read_reference(&a->data.v, store, read_unit_reference, resolve_unit); + int result = read_reference(&a->data.v, data, read_unit_reference, + resolve_unit); if (result == 0 && !a->data.v) { return AT_READ_FAIL; } diff --git a/src/attributes/iceberg.c b/src/attributes/iceberg.c index 06c7c23cf..661a2ec80 100644 --- a/src/attributes/iceberg.c +++ b/src/attributes/iceberg.c @@ -30,6 +30,7 @@ attrib_type at_iceberg = { NULL, a_writeint, a_readint, + NULL, ATF_UNIQUE }; diff --git a/src/attributes/key.c b/src/attributes/key.c index c4873b228..39c7dab66 100644 --- a/src/attributes/key.c +++ b/src/attributes/key.c @@ -22,6 +22,78 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include +#include +#include + +#include +#include +#include + +static void a_writekeys(const attrib *a, const void *o, storage *store) { + int i, *keys = (int *)a->data.v; + for (i = 0; i <= keys[0]; ++i) { + WRITE_INT(store, keys[i]); + } +} + +static int a_readkeys(attrib * a, void *owner, gamedata *data) { + int i, *p = 0; + READ_INT(data->store, &i); + assert(i < 4096 && i>0); + a->data.v = p = malloc(sizeof(int)*(i + 1)); + *p++ = i; + while (i--) { + READ_INT(data->store, p++); + } + return AT_READ_OK; +} + +static int a_readkey(attrib *a, void *owner, struct gamedata *data) { + int res = a_readint(a, owner, data); + return (res != AT_READ_FAIL) ? AT_READ_DEPR : res; +} + +static void a_freekeys(attrib *a) { + free(a->data.v); +} + +attrib_type at_keys = { + "keys", + NULL, + a_freekeys, + NULL, + a_writekeys, + a_readkeys, + NULL +}; + +void a_upgradekeys(attrib **alist, attrib *abegin) { + int n = 0, *keys = 0; + int i = 0, val[4]; + attrib *a, *ak = a_find(*alist, &at_keys); + if (ak) { + keys = (int *)ak->data.v; + if (keys) n = keys[0]; + } + for (a = abegin; a && a->type == abegin->type; a = a->next) { + val[i++] = a->data.i; + if (i == 4) { + keys = realloc(keys, sizeof(int) * (n + i + 1)); + memcpy(keys + n + 1, val, sizeof(int)*i); + n += i; + i = 0; + } + } + if (i > 0) { + keys = realloc(keys, sizeof(int) * (n + i + 1)); + memcpy(keys + n + 1, val, sizeof(int)*i); + if (!ak) { + ak = a_add(alist, a_new(&at_keys)); + } + } + ak->data.v = keys; + keys[0] = n + i; +} attrib_type at_key = { "key", @@ -29,29 +101,61 @@ attrib_type at_key = { NULL, NULL, a_writeint, - a_readint, + a_readkey, + a_upgradekeys }; -attrib *add_key(attrib ** alist, int key) +void key_set(attrib ** alist, int key) { - attrib *a = find_key(*alist, key); - if (a == NULL) - a = a_add(alist, make_key(key)); - return a; -} - -attrib *make_key(int key) -{ - attrib *a = a_new(&at_key); - a->data.i = key; - return a; -} - -attrib *find_key(attrib * alist, int key) -{ - attrib *a = a_find(alist, &at_key); - while (a && a->type == &at_key && a->data.i != key) { - a = a->next; + int *keys, n = 1; + attrib *a; + assert(key != 0); + a = a_find(*alist, &at_keys); + if (!a) { + a = a_add(alist, a_new(&at_keys)); } - return (a && a->type == &at_key) ? a : NULL; + keys = (int *)a->data.v; + if (keys) { + n = keys[0] + 1; + } + keys = realloc(keys, sizeof(int) *(n + 1)); + // TODO: does insertion sort pay off here? + keys[0] = n; + keys[n] = key; + a->data.v = keys; +} + +void key_unset(attrib ** alist, int key) +{ + attrib *a; + assert(key != 0); + a = a_find(*alist, &at_keys); + if (a) { + int i, *keys = (int *)a->data.v; + if (keys) { + for (i = 1; i <= keys[0]; ++i) { + if (keys[i] == key) { + keys[i] = keys[keys[0]]; + keys[0]--; + } + } + } + } +} + +bool key_get(attrib *alist, int key) { + attrib *a; + assert(key != 0); + a = a_find(alist, &at_keys); + if (a) { + int i, *keys = (int *)a->data.v; + if (keys) { + for (i = 1; i <= keys[0]; ++i) { + if (keys[i] == key) { + return true; + } + } + } + } + return false; } diff --git a/src/attributes/key.h b/src/attributes/key.h index 92f409595..89292db1d 100644 --- a/src/attributes/key.h +++ b/src/attributes/key.h @@ -21,12 +21,14 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #ifdef __cplusplus extern "C" { #endif - + struct attrib; + struct attrib_type; extern struct attrib_type at_key; + extern struct attrib_type at_keys; - struct attrib *make_key(int key); - struct attrib *find_key(struct attrib *alist, int key); - struct attrib *add_key(struct attrib **alist, int key); + void key_set(struct attrib **alist, int key); + void key_unset(struct attrib **alist, int key); + bool key_get(struct attrib *alist, int key); #ifdef __cplusplus } diff --git a/src/attributes/key.test.c b/src/attributes/key.test.c new file mode 100644 index 000000000..46343d19f --- /dev/null +++ b/src/attributes/key.test.c @@ -0,0 +1,51 @@ +#include +#include "key.h" + +#include +#include + +static void test_get_set_keys(CuTest *tc) { + attrib *a = 0; + key_set(&a, 42); + key_set(&a, 43); + key_set(&a, 44); + CuAssertTrue(tc, key_get(a, 42)); + CuAssertTrue(tc, key_get(a, 43)); + CuAssertTrue(tc, key_get(a, 44)); + key_unset(&a, 42); + CuAssertTrue(tc, !key_get(a, 42)); + CuAssertTrue(tc, key_get(a, 43)); + CuAssertTrue(tc, key_get(a, 44)); + a_removeall(&a, NULL); +} + +static attrib *key_set_orig(attrib **alist, int key) { + attrib * a = a_add(alist, a_new(&at_key)); + a->data.i = key; + return a; +} + +static void test_upgrade(CuTest *tc) { + attrib *alist = 0; + key_set_orig(&alist, 40); + key_set_orig(&alist, 41); + key_set_orig(&alist, 42); + key_set_orig(&alist, 43); + key_set_orig(&alist, 44); + CuAssertPtrNotNull(tc, alist->type->upgrade); + alist->type->upgrade(&alist, alist); + CuAssertTrue(tc, key_get(alist, 40)); + CuAssertTrue(tc, key_get(alist, 41)); + CuAssertTrue(tc, key_get(alist, 42)); + CuAssertTrue(tc, key_get(alist, 43)); + CuAssertTrue(tc, key_get(alist, 44)); + a_removeall(&alist, NULL); +} + +CuSuite *get_key_suite(void) +{ + CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_get_set_keys); + SUITE_ADD_TEST(suite, test_upgrade); + return suite; +} diff --git a/src/attributes/matmod.c b/src/attributes/matmod.c index 74d39448f..f885de99b 100644 --- a/src/attributes/matmod.c +++ b/src/attributes/matmod.c @@ -28,6 +28,7 @@ attrib_type at_matmod = { NULL, NULL, NULL, + NULL, ATF_PRESERVE }; diff --git a/src/attributes/moved.c b/src/attributes/moved.c index b671646c1..3da2a4c3b 100644 --- a/src/attributes/moved.c +++ b/src/attributes/moved.c @@ -21,6 +21,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "moved.h" #include +#include #include @@ -37,9 +38,9 @@ write_moved(const attrib * a, const void *owner, struct storage *store) WRITE_INT(store, a->data.i); } -static int read_moved(attrib * a, void *owner, struct storage *store) +static int read_moved(attrib * a, void *owner, gamedata *data) { - READ_INT(store, &a->data.i); + READ_INT(data->store, &a->data.i); if (a->data.i != 0) return AT_READ_OK; else diff --git a/src/attributes/movement.c b/src/attributes/movement.c index ca8b6fc2f..0ad08030e 100644 --- a/src/attributes/movement.c +++ b/src/attributes/movement.c @@ -22,6 +22,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include +#include #include @@ -34,9 +35,9 @@ write_movement(const attrib * a, const void *owner, struct storage *store) WRITE_INT(store, a->data.i); } -static int read_movement(attrib * a, void *owner, struct storage *store) +static int read_movement(attrib * a, void *owner, gamedata *data) { - READ_INT(store, &a->data.i); + READ_INT(data->store, &a->data.i); if (a->data.i != 0) return AT_READ_OK; else @@ -49,7 +50,7 @@ attrib_type at_movement = { bool get_movement(attrib * const *alist, int type) { - const attrib *a = a_findc(*alist, &at_movement); + const attrib *a = a_find(*alist, &at_movement); if (a == NULL) return false; if (a->data.i & type) diff --git a/src/attributes/orcification.c b/src/attributes/orcification.c index 4e4877116..dfb8f32f4 100644 --- a/src/attributes/orcification.c +++ b/src/attributes/orcification.c @@ -28,7 +28,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ attrib_type at_orcification = { - "orcification", NULL, NULL, NULL, a_writeint, a_readint, ATF_UNIQUE + "orcification", NULL, NULL, NULL, a_writeint, a_readint, NULL, ATF_UNIQUE }; attrib *make_orcification(int orcification) diff --git a/src/attributes/otherfaction.c b/src/attributes/otherfaction.c index 16682d8f8..83c56e767 100644 --- a/src/attributes/otherfaction.c +++ b/src/attributes/otherfaction.c @@ -24,6 +24,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include #include @@ -38,11 +39,11 @@ void write_of(const struct attrib *a, const void *owner, struct storage *store) WRITE_INT(store, f->no); } -int read_of(struct attrib *a, void *owner, struct storage *store) +int read_of(struct attrib *a, void *owner, gamedata *data) { /* return 1 on success, 0 if attrib needs removal */ int of; - READ_INT(store, &of); + READ_INT(data->store, &of); if (rule_stealth_other()) { a->data.v = findfaction(of); if (a->data.v) { @@ -53,7 +54,7 @@ int read_of(struct attrib *a, void *owner, struct storage *store) } attrib_type at_otherfaction = { - "otherfaction", NULL, NULL, NULL, write_of, read_of, ATF_UNIQUE + "otherfaction", NULL, NULL, NULL, write_of, read_of, NULL, ATF_UNIQUE }; struct faction *get_otherfaction(const struct attrib *a) diff --git a/src/attributes/raceprefix.c b/src/attributes/raceprefix.c index 79d77916d..308c75292 100644 --- a/src/attributes/raceprefix.c +++ b/src/attributes/raceprefix.c @@ -28,7 +28,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include attrib_type at_raceprefix = { - "raceprefix", NULL, a_finalizestring, NULL, a_writestring, a_readstring, + "raceprefix", NULL, a_finalizestring, NULL, a_writestring, a_readstring, NULL, ATF_UNIQUE }; @@ -45,10 +45,10 @@ void set_prefix(attrib ** ap, const char *str) a->data.v = _strdup(str); } -const char *get_prefix(const attrib * a) +const char *get_prefix(attrib * a) { char *str; - a = a_findc(a, &at_raceprefix); + a = a_find(a, &at_raceprefix); if (a == NULL) return NULL; str = (char *)a->data.v; diff --git a/src/attributes/raceprefix.h b/src/attributes/raceprefix.h index 64c81acfc..e1e9f0be0 100644 --- a/src/attributes/raceprefix.h +++ b/src/attributes/raceprefix.h @@ -24,7 +24,7 @@ extern "C" { extern struct attrib_type at_raceprefix; extern void set_prefix(struct attrib **ap, const char *str); - extern const char *get_prefix(const struct attrib *a); + extern const char *get_prefix(struct attrib *a); #ifdef __cplusplus } diff --git a/src/attributes/reduceproduction.c b/src/attributes/reduceproduction.c index 58436bc8e..3f31938d3 100644 --- a/src/attributes/reduceproduction.c +++ b/src/attributes/reduceproduction.c @@ -49,6 +49,7 @@ attrib_type at_reduceproduction = { age_reduceproduction, a_writeshorts, a_readshorts, + NULL, ATF_UNIQUE }; diff --git a/src/attributes/targetregion.c b/src/attributes/targetregion.c index d11af192c..b293b458c 100644 --- a/src/attributes/targetregion.c +++ b/src/attributes/targetregion.c @@ -25,6 +25,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include +#include #include #include @@ -35,11 +36,10 @@ write_targetregion(const attrib * a, const void *owner, struct storage *store) write_region_reference((region *)a->data.v, store); } -static int read_targetregion(attrib * a, void *owner, struct storage *store) +static int read_targetregion(attrib * a, void *owner, gamedata *data) { - int result = - read_reference(&a->data.v, store, read_region_reference, - RESOLVE_REGION(global.data_version)); + int result = read_reference(&a->data.v, data, read_region_reference, + RESOLVE_REGION(data->version)); if (result == 0 && !a->data.v) return AT_READ_FAIL; return AT_READ_OK; @@ -52,6 +52,7 @@ attrib_type at_targetregion = { NULL, write_targetregion, read_targetregion, + NULL, ATF_UNIQUE }; diff --git a/src/battle.c b/src/battle.c index 8fb2ec252..9d6448be4 100644 --- a/src/battle.c +++ b/src/battle.c @@ -690,7 +690,7 @@ static int CavalryBonus(const unit * u, troop enemy, int type) int skl = effskill(u, SK_RIDING, 0); /* only half against trolls */ if (skl > 0) { - if (type == BONUS_DAMAGE) { + if (type == BONUS_SKILL) { int dmg = _min(skl, 8); if (u_race(enemy.fighter->unit) == get_race(RC_TROLL)) { dmg = dmg / 4; @@ -1156,7 +1156,6 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile) unit *au = af->unit; unit *du = df->unit; battle *b = df->side->battle; - int heiltrank = 0; /* Schild */ side *ds = df->side; @@ -1289,7 +1288,7 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile) } } - assert(dt.index < du->number); + assert(dt.index >= 0 && dt.index < du->number); if (rda>0) { df->person[dt.index].hp -= rda; if (u_race(au) == get_race(RC_DAEMON)) { @@ -1314,36 +1313,23 @@ terminate(troop dt, troop at, int type, const char *damage, bool missile) df->person[dt.index].defence--; } } - df->person[dt.index].flags = (df->person[dt.index].flags & ~FL_SLEEPING); return false; } /* Sieben Leben */ if (u_race(du) == get_race(RC_CAT) && (chance(1.0 / 7))) { - assert(dt.index >= 0 && dt.index < du->number); df->person[dt.index].hp = unit_max_hp(du); return false; } - /* Heiltrank schluerfen und hoffen */ - if (oldpotiontype[P_HEAL]) { - if (get_effect(du, oldpotiontype[P_HEAL]) > 0) { - change_effect(du, oldpotiontype[P_HEAL], -1); - heiltrank = 1; - } - else if (i_get(du->items, oldpotiontype[P_HEAL]->itype) > 0) { + if (oldpotiontype[P_HEAL] && !fval(&df->person[dt.index], FL_HEALING_USED)) { + if (i_get(du->items, oldpotiontype[P_HEAL]->itype) > 0) { i_change(&du->items, oldpotiontype[P_HEAL]->itype, -1); - change_effect(du, oldpotiontype[P_HEAL], 3); - heiltrank = 1; - } - if (heiltrank && (chance(0.50))) { - { - message *m = msg_message("battle::potionsave", "unit", du); - message_faction(b, du->faction, m); - msg_release(m); - } - assert(dt.index >= 0 && dt.index < du->number); - df->person[dt.index].hp = u_race(du)->hitpoints; + message *m = msg_message("battle::potionsave", "unit", du); + message_faction(b, du->faction, m); + msg_release(m); + fset(&df->person[dt.index], FL_HEALING_USED); + df->person[dt.index].hp = u_race(du)->hitpoints * 5; /* give the person a buffer */ return false; } } @@ -2332,7 +2318,7 @@ void do_attack(fighter * af) if (apr > 0) { /* Wenn die Waffe nachladen muss, oder es sich nicht um einen * Waffen-Angriff handelt, dann gilt der Speed nicht. */ - /* FIXME allow multiple AT_NATURAL attacks? */ + /* TODO: allow multiple AT_NATURAL attacks? */ if (u_race(au)->attack[a].type != AT_STANDARD) continue; else { @@ -2384,7 +2370,7 @@ static void add_tactics(tactics * ta, fighter * fig, int value) ta->value = value; } -static double horsebonus(const unit * u) +static double horse_fleeing_bonus(const unit * u) { const item_type *it_horse, *it_elvenhorse, *it_charger; int n1 = 0, n2 = 0, n3 = 0; @@ -2392,8 +2378,6 @@ static double horsebonus(const unit * u) int skl = effskill(u, SK_RIDING, 0); const resource_type *rtype; - if (skl < 1) return 0.0; - it_horse = ((rtype = get_resourcetype(R_HORSE)) != NULL) ? rtype->itype : 0; it_elvenhorse = ((rtype = get_resourcetype(R_UNICORN)) != NULL) ? rtype->itype : 0; it_charger = ((rtype = get_resourcetype(R_CHARGER)) != NULL) ? rtype->itype : 0; @@ -2410,9 +2394,9 @@ static double horsebonus(const unit * u) } if (skl >= 5 && n3 >= u->number) return 0.30; - if (skl >= 3 && n2 + n3 >= u->number) + if (skl >= 2 && n2 + n3 >= u->number) return 0.20; - if (skl >= 1 && n1 + n2 + n3 >= u->number) + if (n1 + n2 + n3 >= u->number) return 0.10; return 0.0F; } @@ -2424,7 +2408,7 @@ double fleechance(unit * u) /* Einheit u versucht, dem Getümmel zu entkommen */ c += (effskill(u, SK_STEALTH, 0) * 0.05); - c += horsebonus(u); + c += horse_fleeing_bonus(u); if (u_race(u) == get_race(RC_HALFLING)) { c += 0.20; @@ -3585,14 +3569,15 @@ battle *make_battle(region * r) if (battledebug) { char zText[MAX_PATH]; char zFilename[MAX_PATH]; - sprintf(zText, "%s/battles", basepath()); + join_path(basepath(), "battles", zText, sizeof(zText)); if (_mkdir(zText) != 0) { log_error("could not create subdirectory for battle logs: %s", zText); battledebug = false; } else { - sprintf(zFilename, "%s/battle-%d-%s.log", zText, obs_count++, simplename(r)); - bdebug = fopen(zFilename, "w"); + sprintf(zFilename, "battle-%d-%s.log", obs_count++, simplename(r)); + join_path(zText, zFilename, zText, sizeof(zText)); + bdebug = fopen(zText, "w"); if (!bdebug) log_error("battles cannot be debugged"); else { diff --git a/src/battle.h b/src/battle.h index 4ea3834d9..3e156070d 100644 --- a/src/battle.h +++ b/src/battle.h @@ -137,6 +137,7 @@ extern "C" { #define FL_SLEEPING 16 #define FL_STUNNED 32 /* eine Runde keinen Angriff */ #define FL_HIT 64 /* the person at attacked */ +#define FL_HEALING_USED 128 /* has used a healing potion */ typedef struct troop { struct fighter *fighter; diff --git a/src/bind_building.c b/src/bind_building.c index a3ecb5913..3724d4db5 100644 --- a/src/bind_building.c +++ b/src/bind_building.c @@ -52,15 +52,15 @@ static int tolua_building_set_working(lua_State * L) { building *self = (building *)tolua_tousertype(L, 1, 0); bool flag = !!lua_toboolean(L, 2); - if (flag) self->flags |= BLD_WORKING; - else self->flags &= ~BLD_WORKING; + if (flag) self->flags |= BLD_MAINTAINED; + else self->flags &= ~BLD_MAINTAINED; return 1; } static int tolua_building_get_working(lua_State * L) { building *self = (building *)tolua_tousertype(L, 1, 0); - bool flag = (self->flags&BLD_WORKING) != 0; + bool flag = (self->flags&BLD_MAINTAINED) != 0; lua_pushboolean(L, flag); return 1; } diff --git a/src/bind_config.c b/src/bind_config.c index 945c7f603..3959cc22c 100644 --- a/src/bind_config.c +++ b/src/bind_config.c @@ -1,6 +1,7 @@ #include "bind_config.h" #include +#include #include #include #include @@ -58,11 +59,11 @@ int config_read(const char *filename, const char * relpath) json_relpath = relpath; if (relpath) { - _snprintf(name, sizeof(name), "%s/%s", relpath, filename); - F = fopen(name, "rt"); + join_path(relpath, filename, name, sizeof(name)); + F = fopen(name, "r"); } else { - F = fopen(filename, "rt"); + F = fopen(filename, "r"); } if (F) { long size; diff --git a/src/bind_eressea.c b/src/bind_eressea.c index 270cb44e8..d7683ec18 100755 --- a/src/bind_eressea.c +++ b/src/bind_eressea.c @@ -32,7 +32,7 @@ int eressea_read_orders(const char * filename) { } int eressea_export_json(const char * filename, int flags) { - FILE *F = fopen(filename, "wt"); + FILE *F = fopen(filename, "w"); if (F) { stream out = { 0 }; int err; @@ -46,7 +46,7 @@ int eressea_export_json(const char * filename, int flags) { } int eressea_import_json(const char * filename) { - FILE *F = fopen(filename, "rt"); + FILE *F = fopen(filename, "r"); if (F) { stream out = { 0 }; int err; diff --git a/src/bind_process.c b/src/bind_process.c index 7d0124400..e8420c654 100755 --- a/src/bind_process.c +++ b/src/bind_process.c @@ -274,7 +274,7 @@ void process_maintenance(void) { } } } - maintain_buildings(r, 0); + maintain_buildings(r); } } diff --git a/src/bind_region.c b/src/bind_region.c index a944b7c30..e56c3443c 100644 --- a/src/bind_region.c +++ b/src/bind_region.c @@ -77,8 +77,8 @@ static int tolua_region_set_blocked(lua_State * L) { region *self = (region *)tolua_tousertype(L, 1, 0); bool flag = !!tolua_toboolean(L, 2, 1); - if (flag) self->flags |= BLD_WORKING; - else self->flags &= ~BLD_WORKING; + if (flag) self->flags |= RF_BLOCKED; + else self->flags &= ~RF_BLOCKED; return 0; } @@ -458,6 +458,7 @@ static int tolua_region_create(lua_State * L) const terrain_type *terrain = get_terrain(tname); region *r, *result; if (!terrain) { + log_error("lua: region.create with invalid terrain %s", tname); return 0; } @@ -481,6 +482,7 @@ static int tolua_region_create(lua_State * L) tolua_pushusertype(L, result, TOLUA_CAST "region"); return 1; } + log_error("lua: region.create with invalid terrain %s", tname); return 0; } @@ -542,11 +544,9 @@ static int tolua_region_getkey(lua_State * L) { region *self = (region *)tolua_tousertype(L, 1, 0); const char *name = tolua_tostring(L, 2, 0); - int flag = atoi36(name); - attrib *a = find_key(self->attribs, flag); - lua_pushboolean(L, a != NULL); + lua_pushboolean(L, key_get(self->attribs, flag)); return 1; } @@ -555,14 +555,13 @@ static int tolua_region_setkey(lua_State * L) region *self = (region *)tolua_tousertype(L, 1, 0); const char *name = tolua_tostring(L, 2, 0); int value = tolua_toboolean(L, 3, 0); - int flag = atoi36(name); - attrib *a = find_key(self->attribs, flag); - if (a == NULL && value) { - add_key(&self->attribs, flag); + + if (value) { + key_set(&self->attribs, flag); } - else if (a != NULL && !value) { - a_remove(&self->attribs, a); + else { + key_unset(&self->attribs, flag); } return 0; } @@ -583,6 +582,13 @@ static int tolua_plane_get(lua_State * L) return 1; } +static int tolua_plane_erase(lua_State *L) +{ + plane *self = (plane *)tolua_tousertype(L, 1, 0); + remove_plane(self); + return 0; +} + static int tolua_plane_create(lua_State * L) { int id = (int)tolua_tonumber(L, 1, 0); @@ -740,6 +746,7 @@ void tolua_region_open(lua_State * L) tolua_beginmodule(L, TOLUA_CAST "plane"); { tolua_function(L, TOLUA_CAST "create", tolua_plane_create); + tolua_function(L, TOLUA_CAST "erase", tolua_plane_erase); tolua_function(L, TOLUA_CAST "get", tolua_plane_get); tolua_function(L, TOLUA_CAST "__tostring", tolua_plane_tostring); diff --git a/src/bind_storage.c b/src/bind_storage.c index aaeb84128..5dfca8c89 100644 --- a/src/bind_storage.c +++ b/src/bind_storage.c @@ -17,6 +17,9 @@ without prior permission by the authors of Eressea. #include #include +#include +#include + #include #include #include @@ -26,20 +29,22 @@ without prior permission by the authors of Eressea. #include #include #include +#include #include static int tolua_storage_create(lua_State * L) { const char *filename = tolua_tostring(L, 1, 0); - const char *type = tolua_tostring(L, 2, "r"); + const char *type = tolua_tostring(L, 2, "rb"); gamedata *data; - data = gamedata_open(filename, type); + data = gamedata_open(filename, type, RELEASE_VERSION); if (data) { tolua_pushusertype(L, (void *)data, TOLUA_CAST "storage"); return 1; } + log_error("could not open %s, mode %s (%s).", filename, type, strerror(errno)); return 0; } @@ -97,8 +102,7 @@ static int tolua_storage_tostring(lua_State * L) { gamedata *data = (gamedata *)tolua_tousertype(L, 1, 0); char name[64]; - _snprintf(name, sizeof(name), "", data->encoding, - data->version); + _snprintf(name, sizeof(name), "", (void *)data, data->version); lua_pushstring(L, name); return 1; } diff --git a/src/bind_unit.c b/src/bind_unit.c index e0b076710..353bd4667 100755 --- a/src/bind_unit.c +++ b/src/bind_unit.c @@ -807,8 +807,7 @@ static int tolua_unit_get_flag(lua_State * L) unit *self = (unit *)tolua_tousertype(L, 1, 0); const char *name = tolua_tostring(L, 2, 0); int flag = atoi36(name); - attrib *a = find_key(self->attribs, flag); - lua_pushboolean(L, (a != NULL)); + lua_pushboolean(L, key_get(self->attribs, flag)); return 1; } @@ -818,12 +817,11 @@ static int tolua_unit_set_flag(lua_State * L) const char *name = tolua_tostring(L, 2, 0); int value = (int)tolua_tonumber(L, 3, 0); int flag = atoi36(name); - attrib *a = find_key(self->attribs, flag); - if (a == NULL && value) { - add_key(&self->attribs, flag); + if (value) { + key_set(&self->attribs, flag); } - else if (a != NULL && !value) { - a_remove(&self->attribs, a); + else { + key_unset(&self->attribs, flag); } return 0; } @@ -891,19 +889,15 @@ static int tolua_unit_create(lua_State * L) { faction *f = (faction *)tolua_tousertype(L, 1, 0); region *r = (region *)tolua_tousertype(L, 2, 0); + const char *rcname = tolua_tostring(L, 4, NULL); int num = (int)tolua_tonumber(L, 3, 1); - if (f && r) { - const race *rc = f->race; - const char *rcname = tolua_tostring(L, 4, NULL); - if (rcname) - rc = rc_find(rcname); - if (rc) { - unit *u = create_unit(r, f, num, rc, 0, NULL, NULL); - tolua_pushusertype(L, u, TOLUA_CAST "unit"); - return 1; - } - } - return 0; + const race *rc; + assert(f && r); + rc = rcname ? rc_find(rcname) : f->race; + assert(rc); + unit *u = create_unit(r, f, num, rc, 0, NULL, NULL); + tolua_pushusertype(L, u, TOLUA_CAST "unit"); + return 1; } static int tolua_unit_tostring(lua_State * L) diff --git a/src/bindings.c b/src/bindings.c index 938b75be8..b95b1f9f0 100755 --- a/src/bindings.c +++ b/src/bindings.c @@ -27,6 +27,7 @@ without prior permission by the authors of Eressea. #include "console.h" #include "reports.h" #include "seen.h" +#include "study.h" #include "calendar.h" #include @@ -96,7 +97,7 @@ TOLUA_PKG(game); int log_lua_error(lua_State * L) { const char *error = lua_tostring(L, -1); - log_fatal("LUA call failed.\n%s\n", error); + log_fatal("Lua call failed.\n%s\n", error); lua_pop(L, 1); return 1; } @@ -186,8 +187,8 @@ static int tolua_getkey(lua_State * L) { const char *name = tolua_tostring(L, 1, 0); int flag = atoi36(name); - attrib *a = find_key(global.attribs, flag); - lua_pushboolean(L, a != NULL); + + lua_pushboolean(L, key_get(global.attribs, flag)); return 1; } @@ -209,12 +210,11 @@ static int tolua_setkey(lua_State * L) const char *name = tolua_tostring(L, 1, 0); int value = tolua_toboolean(L, 2, 0); int flag = atoi36(name); - attrib *a = find_key(global.attribs, flag); - if (a == NULL && value) { - add_key(&global.attribs, flag); + if (value) { + key_set(&global.attribs, flag); } - else if (a != NULL && !value) { - a_remove(&global.attribs, a); + else { + key_unset(&global.attribs, flag); } return 0; } @@ -394,10 +394,10 @@ static int tolua_learn_skill(lua_State * L) { unit *u = (unit *)tolua_tousertype(L, 1, 0); const char *skname = tolua_tostring(L, 2, 0); - float chances = (float)tolua_tonumber(L, 3, 0); + int days = (int)tolua_tonumber(L, 3, 0); skill_t sk = findskill(skname); if (sk != NOSKILL) { - learn_skill(u, sk, chances); + learn_skill(u, sk, days); } return 0; } @@ -448,7 +448,7 @@ static int tolua_equipunit(lua_State * L) unit *u = (unit *)tolua_tousertype(L, 1, 0); const char *eqname = tolua_tostring(L, 2, 0); int mask = (int)tolua_tonumber(L, 3, EQUIP_ALL); - assert(mask > 0); + assert(u && mask > 0); equip_unit_mask(u, get_equipment(eqname), mask); return 0; } @@ -1184,6 +1184,7 @@ int eressea_run(lua_State *L, const char *luafile) err = lua_pcall(L, 1, 1, -3); if (err != 0) { log_lua_error(L); + assert(!"Lua syntax error? check log."); } else { if (lua_isnumber(L, -1)) { diff --git a/src/building_action.c b/src/building_action.c index 29c20a99d..b6ce0f72c 100644 --- a/src/building_action.c +++ b/src/building_action.c @@ -15,6 +15,7 @@ without prior permission by the authors of Eressea. #include #include #include +#include #include #include @@ -98,14 +99,15 @@ lc_write(const struct attrib *a, const void *owner, struct storage *store) WRITE_TOK(store, fparam ? fparam : NULLSTRING); } -static int lc_read(struct attrib *a, void *owner, struct storage *store) +static int lc_read(struct attrib *a, void *owner, gamedata *data) { + struct storage *store = data->store; char name[NAMESIZE]; - building_action *data = (building_action *)a->data.v; + building_action *bd = (building_action *)a->data.v; building *b = (building *)owner; int result = 0; - if (global.data_version < ATTRIBOWNER_VERSION) { - result = read_reference(&b, store, read_building_reference, resolve_building); + if (data->version < ATTRIBOWNER_VERSION) { + result = read_reference(&b, data, read_building_reference, resolve_building); assert(b == owner); } READ_TOK(store, name, sizeof(name)); @@ -115,7 +117,7 @@ static int lc_read(struct attrib *a, void *owner, struct storage *store) b = 0; } else { - data->fname = _strdup(name); + bd->fname = _strdup(name); } READ_TOK(store, name, sizeof(name)); if (strcmp(name, "tnnL") == 0) { @@ -124,9 +126,9 @@ static int lc_read(struct attrib *a, void *owner, struct storage *store) b = 0; } if (strcmp(name, NULLSTRING) == 0) - data->param = 0; + bd->param = 0; else { - data->param = _strdup(name); + bd->param = _strdup(name); } if (result == 0 && !b) { return AT_READ_FAIL; diff --git a/src/buildno.h b/src/buildno.h index c84827cec..f9749c33c 100644 --- a/src/buildno.h +++ b/src/buildno.h @@ -1,3 +1,3 @@ #define VERSION_MAJOR 3 -#define VERSION_MINOR 8 -#define VERSION_BUILD 16 +#define VERSION_MINOR 9 +#define VERSION_BUILD 0 diff --git a/src/chaos.c b/src/chaos.c index a84b26dfc..afa787b29 100644 --- a/src/chaos.c +++ b/src/chaos.c @@ -50,6 +50,7 @@ attrib_type at_chaoscount = { DEFAULT_AGE, a_writeint, a_readint, + NULL, ATF_UNIQUE }; diff --git a/src/creport.c b/src/creport.c index 591061f94..6ba9f5e39 100644 --- a/src/creport.c +++ b/src/creport.c @@ -1160,7 +1160,8 @@ int seemode, FILE * F) } } if (cs) { - const char *bname = mkname("border", b->type->name(b, r, f, GF_PURE)); + 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, "%d;richtung\n", d); @@ -1498,7 +1499,7 @@ report_computer(const char *filename, report_context * ctx, const char *charset) const char *mailto = LOC(f->locale, "mailto"); const attrib *a; seen_region *sr = NULL; - FILE *F = fopen(filename, "wt"); + FILE *F = fopen(filename, "w"); if (era < 0) { era = config_get_int("world.era", 1); diff --git a/src/direction.test.c b/src/direction.test.c index fc2dea46e..c2feebd3f 100644 --- a/src/direction.test.c +++ b/src/direction.test.c @@ -11,7 +11,7 @@ static void test_init_directions(CuTest *tc) { struct locale *lang; test_cleanup(); - lang = get_or_create_locale("en"); + lang = get_or_create_locale("de"); locale_setstring(lang, "dir_nw", "NW"); init_directions(lang); CuAssertIntEquals(tc, D_NORTHWEST, get_direction("nw", lang)); diff --git a/src/economy.c b/src/economy.c index 6311d0c3a..155a46b7e 100644 --- a/src/economy.c +++ b/src/economy.c @@ -28,6 +28,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "laws.h" #include "randenc.h" #include "spy.h" +#include "study.h" #include "move.h" #include "monster.h" #include "morale.h" @@ -708,33 +709,29 @@ static int forget_cmd(unit * u, order * ord) return 0; } -static bool maintain(building * b, bool first) -/* first==false -> take money from wherever you can */ +static int maintain(building * b) { - const resource_type *rsilver = get_resourcetype(R_SILVER); int c; region *r = b->region; - bool paid = true, work = first; + bool paid = true, work = true; unit *u; - if (fval(b, BLD_MAINTAINED) || b->type == NULL || b->type->maintenance == NULL || is_cursed(b->attribs, C_NOCOST, 0)) { - fset(b, BLD_MAINTAINED); - fset(b, BLD_WORKING); - return true; + if (fval(b, BLD_MAINTAINED) || b->type == NULL || b->type->maintenance == NULL) { + return BLD_MAINTAINED; } if (fval(b, BLD_DONTPAY)) { - return false; + return 0; } u = building_owner(b); if (u == NULL) { /* no owner - send a message to the entire region */ ADDMSG(&r->msgs, msg_message("maintenance_noowner", "building", b)); - return false; + return 0; } /* If the owner is the region owner, check if dontpay flag is set for the building where he is in */ if (config_token("rules.region_owner_pay_building", b->type->_name)) { if (fval(u->building, BLD_DONTPAY)) { - return false; + return 0; } } for (c = 0; b->type->maintenance[c].number; ++c) { @@ -747,25 +744,7 @@ static bool maintain(building * b, bool first) /* first ist im ersten versuch true, im zweiten aber false! Das * bedeutet, das in der Runde in die Region geschafften Resourcen * nicht genutzt werden können, weil die reserviert sind! */ - if (!first) - need -= get_pooled(u, m->rtype, GET_ALL, need); - else - need -= get_pooled(u, m->rtype, GET_DEFAULT, need); - if (!first && need > 0) { - unit *ua; - for (ua = r->units; ua; ua = ua->next) - freset(ua->faction, FFL_SELECT); - fset(u->faction, FFL_SELECT); /* hat schon */ - for (ua = r->units; ua; ua = ua->next) { - if (!fval(ua->faction, FFL_SELECT) && (ua->faction == u->faction - || alliedunit(ua, u->faction, HELP_MONEY))) { - need -= get_pooled(ua, m->rtype, GET_ALL, need); - fset(ua->faction, FFL_SELECT); - if (need <= 0) - break; - } - } - } + need -= get_pooled(u, m->rtype, GET_DEFAULT, need); } if (need > 0) { if (!fval(m, MTF_VITAL)) @@ -777,11 +756,12 @@ static bool maintain(building * b, bool first) } } if (fval(b, BLD_DONTPAY)) { - return false; + return 0; } u = building_owner(b); - if (u == NULL) - return false; + if (!u) { + return 0; + } for (c = 0; b->type->maintenance[c].number; ++c) { const maintenance *m = b->type->maintenance + c; int need = m->number; @@ -789,32 +769,10 @@ static bool maintain(building * b, bool first) if (fval(m, MTF_VARIABLE)) need = need * b->size; if (u) { - /* first ist im ersten versuch true, im zweiten aber false! Das - * bedeutet, das in der Runde in die Region geschafften Resourcen - * nicht genutzt werden können, weil die reserviert sind! */ - if (!first) - need -= get_pooled(u, m->rtype, GET_ALL, need); - else - need -= get_pooled(u, m->rtype, GET_DEFAULT, need); - if (!first && need > 0) { - unit *ua; - for (ua = r->units; ua; ua = ua->next) - freset(ua->faction, FFL_SELECT); - fset(u->faction, FFL_SELECT); /* hat schon */ - for (ua = r->units; ua; ua = ua->next) { - if (!fval(ua->faction, FFL_SELECT) && (ua->faction == u->faction - || alliedunit(ua, u->faction, HELP_MONEY))) { - need -= get_pooled(ua, m->rtype, GET_ALL, need); - fset(ua->faction, FFL_SELECT); - if (need <= 0) - break; - } - } - } + need -= get_pooled(u, m->rtype, GET_DEFAULT, need); if (need > 0) { work = false; - if (fval(m, MTF_VITAL)) - { + if (fval(m, MTF_VITAL)) { paid = false; break; } @@ -822,20 +780,9 @@ static bool maintain(building * b, bool first) } } if (paid && c > 0) { - /* TODO: wieviel von was wurde bezahlt */ - if (first && work) { - ADDMSG(&u->faction->msgs, msg_message("maintenance", "unit building", u, b)); - fset(b, BLD_WORKING); - fset(b, BLD_MAINTAINED); - } - if (!first) { - ADDMSG(&u->faction->msgs, msg_message("maintenance_late", "building", b)); - fset(b, BLD_MAINTAINED); - } - - if (first && !work) { + if (!work) { ADDMSG(&u->faction->msgs, msg_message("maintenancefail", "unit building", u, b)); - return false; + return 0; } for (c = 0; b->type->maintenance[c].number; ++c) { @@ -847,66 +794,44 @@ static bool maintain(building * b, bool first) if (fval(m, MTF_VARIABLE)) cost = cost * b->size; - if (!first) - cost -= use_pooled(u, m->rtype, GET_ALL, cost); - else - cost -= + cost -= use_pooled(u, m->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK, cost); - if (!first && cost > 0) { - unit *ua; - for (ua = r->units; ua; ua = ua->next) - freset(ua->faction, FFL_SELECT); - fset(u->faction, FFL_SELECT); /* hat schon */ - for (ua = r->units; ua; ua = ua->next) { - if (!fval(ua->faction, FFL_SELECT) - && alliedunit(ua, u->faction, HELP_MONEY)) { - int give = use_pooled(ua, m->rtype, GET_ALL, cost); - if (!give) - continue; - cost -= give; - fset(ua->faction, FFL_SELECT); - if (m->rtype == rsilver) - add_donation(ua->faction, u->faction, give, r); - if (cost <= 0) - break; - } - } - } assert(cost == 0); } + if (work) { + ADDMSG(&u->faction->msgs, msg_message("maintenance", "unit building", u, b)); + return BLD_MAINTAINED; + } } - else { - ADDMSG(&u->faction->msgs, msg_message("maintenancefail", "unit building", u, b)); - return false; - } - return true; + ADDMSG(&u->faction->msgs, msg_message("maintenancefail", "unit building", u, b)); + return 0; } -void maintain_buildings(region * r, bool crash) +void maintain_buildings(region * r) { + const curse_type *nocost_ct = ct_find("nocostbuilding"); building **bp = &r->buildings; while (*bp) { building *b = *bp; - bool maintained = maintain(b, !crash); + int flags = BLD_MAINTAINED; + + if (!curse_active(get_curse(b->attribs, nocost_ct))) { + flags = maintain(b); + } + fset(b, flags); - /* the second time, send a message */ - if (crash) { - if (!fval(b, BLD_WORKING)) { - unit *u = building_owner(b); - const char *msgtype = - maintained ? "maintenance_nowork" : "maintenance_none"; - struct message *msg = msg_message(msgtype, "building", b); - - if (u) { - add_message(&u->faction->msgs, msg); - r_addmessage(r, u->faction, msg); - } - else { - add_message(&r->msgs, msg); - } - msg_release(msg); + if (!fval(b, BLD_MAINTAINED)) { + unit *u = building_owner(b); + struct message *msg = msg_message("maintenance_nowork", "building", b); + if (u) { + add_message(&u->faction->msgs, msg); + r_addmessage(r, u->faction, msg); } + else { + add_message(&r->msgs, msg); + } + msg_release(msg); } bp = &b->next; } diff --git a/src/economy.h b/src/economy.h index 7e399cc76..b6c8002c9 100644 --- a/src/economy.h +++ b/src/economy.h @@ -54,10 +54,9 @@ extern "C" { void auto_work(struct region *r); enum { IC_WORK, IC_ENTERTAIN, IC_TAX, IC_TRADE, IC_TRADETAX, IC_STEAL, IC_MAGIC, IC_LOOT }; - void maintain_buildings(struct region *r, bool crash); + void maintain_buildings(struct region *r); int make_cmd(struct unit *u, struct order *ord); void split_allocations(struct region *r); - int recruit_archetypes(void); int give_control_cmd(struct unit *u, struct order *ord); void give_control(struct unit * u, struct unit * u2); void tax_cmd(struct unit * u, struct order *ord, struct request ** taxorders); diff --git a/src/economy.test.c b/src/economy.test.c index 577c7931a..ce09aae43 100644 --- a/src/economy.test.c +++ b/src/economy.test.c @@ -222,8 +222,6 @@ static void test_tax_cmd(CuTest *tc) { CuAssertPtrEquals(tc, 0, test_find_messagetype(u->faction->msgs, "error_no_tax_skill")); CuAssertPtrNotNull(tc, taxorders); - - rsetmoney(r, 11); expandtax(r, taxorders); CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "income")); @@ -243,6 +241,55 @@ static void test_tax_cmd(CuTest *tc) { test_cleanup(); } +static void test_maintain_buildings(CuTest *tc) { + region *r; + building *b; + building_type *btype; + unit *u; + maintenance *req; + item_type *itype; + + test_cleanup(); + btype = test_create_buildingtype("Hort"); + btype->maxsize = 10; + r = test_create_region(0, 0, 0); + u = test_create_unit(test_create_faction(0), r); + b = test_create_building(r, btype); + itype = test_create_itemtype("money"); + b->size = btype->maxsize; + u_set_building(u, b); + + // this building has no upkeep, it just works: + b->flags = 0; + maintain_buildings(r); + CuAssertIntEquals(tc, BLD_MAINTAINED, fval(b, BLD_MAINTAINED)); + + req = calloc(2, sizeof(maintenance)); + req[0].number = 100; + req[0].rtype = itype->rtype; + btype->maintenance = req; + + // we cannot afford to pay: + b->flags = 0; + maintain_buildings(r); + CuAssertIntEquals(tc, 0, fval(b, BLD_MAINTAINED)); + + // we can afford to pay: + i_change(&u->items, itype, 100); + b->flags = 0; + maintain_buildings(r); + CuAssertIntEquals(tc, BLD_MAINTAINED, fval(b, BLD_MAINTAINED)); + CuAssertIntEquals(tc, 0, i_get(u->items, itype)); + + // this building has no owner, it doesn't work: + u_set_building(u, NULL); + b->flags = 0; + maintain_buildings(r); + CuAssertIntEquals(tc, 0, fval(b, BLD_MAINTAINED)); + + test_cleanup(); +} + CuSuite *get_economy_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -254,5 +301,6 @@ CuSuite *get_economy_suite(void) SUITE_ADD_TEST(suite, test_normals_recruit); SUITE_ADD_TEST(suite, test_heroes_dont_recruit); SUITE_ADD_TEST(suite, test_tax_cmd); + SUITE_ADD_TEST(suite, test_maintain_buildings); return suite; } diff --git a/src/eressea.c b/src/eressea.c index d08a60df6..18c98cad6 100755 --- a/src/eressea.c +++ b/src/eressea.c @@ -49,7 +49,9 @@ void game_done(void) calendar_cleanup(); #endif free_functions(); - free_curses(); + free_config(); + free_locales(); + curses_done(); kernel_done(); } diff --git a/src/give.c b/src/give.c index 52b1cda34..80f4a1ab5 100644 --- a/src/give.c +++ b/src/give.c @@ -70,7 +70,11 @@ static void feedback_give_not_allowed(unit * u, order * ord) static bool can_give(const unit * u, const unit * u2, const item_type * itype, int mask) { if (u2) { - if (u->faction != u2->faction) { + if (u2->number==0 && !fval(u2, UFL_ISNEW)) { + // https://bugs.eressea.de/view.php?id=2230 + // cannot give anything to dead units + return false; + } else if (u->faction != u2->faction) { int rule = rule_give(); if (itype) { assert(mask == 0); diff --git a/src/give.test.c b/src/give.test.c index 119c5f1a9..e65c2861c 100644 --- a/src/give.test.c +++ b/src/give.test.c @@ -31,18 +31,11 @@ struct give { static void setup_give(struct give *env) { struct terrain_type *ter = test_create_terrain("plain", LAND_REGION); - struct locale *lang; race *rc; assert(env->f1); rc = test_create_race(env->f1->race ? env->f1->race->_name : "humon"); rc->ec_flags |= GIVEPERSON; - lang = get_or_create_locale(env->f1->locale ? locale_name(env->f1->locale) : "test"); - env->f1->locale = lang; - locale_setstring(lang, "ALLES", "ALLES"); - locale_setstring(lang, "PERSONEN", "PERSONEN"); - locale_setstring(lang, "KRAEUTER", "KRAUT"); - init_locale(lang); env->r = test_create_region(0, 0, ter); env->src = test_create_unit(env->f1, env->r); @@ -277,7 +270,6 @@ static void test_give(CuTest * tc) { static void test_give_herbs(CuTest * tc) { struct give env; struct order *ord; - struct locale * lang; char cmd[32]; test_cleanup(); @@ -286,12 +278,8 @@ static void test_give_herbs(CuTest * tc) { setup_give(&env); i_change(&env.src->items, env.itype, 10); - lang = get_or_create_locale("test"); - env.f1->locale = lang; - locale_setstring(lang, "KRAEUTER", "KRAUT"); - init_locale(lang); - _snprintf(cmd, sizeof(cmd), "%s KRAUT", itoa36(env.dst->no)); - ord = create_order(K_GIVE, lang, cmd); + _snprintf(cmd, sizeof(cmd), "%s %s", itoa36(env.dst->no), LOC(env.f1->locale, parameters[P_HERBS])); + ord = create_order(K_GIVE, env.f1->locale, cmd); assert(ord); give_cmd(env.src, ord); @@ -328,11 +316,38 @@ static void test_give_denied_by_rules(CuTest * tc) { test_cleanup(); } +static void test_give_dead_unit(CuTest * tc) { + struct give env; + struct message *msg; + + test_cleanup(); + env.f1 = test_create_faction(0); + env.f2 = test_create_faction(0); + setup_give(&env); + env.dst->number = 0; + freset(env.dst, UFL_ISNEW); + CuAssertPtrNotNull(tc, msg = check_give(env.src, env.dst, 0)); + msg_release(msg); + test_cleanup(); +} + +static void test_give_new_unit(CuTest * tc) { + struct give env; + + test_cleanup(); + env.f1 = test_create_faction(0); + env.f2 = test_create_faction(0); + setup_give(&env); + env.dst->number = 0; + fset(env.dst, UFL_ISNEW); + CuAssertPtrEquals(tc, 0, check_give(env.src, env.dst, 0)); + test_cleanup(); +} + static void test_give_invalid_target(CuTest *tc) { // bug https://bugs.eressea.de/view.php?id=1685 struct give env; order *ord; - struct locale * lang; test_cleanup(); env.f1 = test_create_faction(0); @@ -340,11 +355,7 @@ static void test_give_invalid_target(CuTest *tc) { setup_give(&env); i_change(&env.src->items, env.itype, 10); - lang = get_or_create_locale("de"); - env.f1->locale = lang; - locale_setstring(lang, "KRAEUTER", "KRAUT"); - init_locale(lang); - ord = create_order(K_GIVE, lang, "## KRAUT"); + ord = create_order(K_GIVE, env.f1->locale, "## KRAUT"); assert(ord); give_cmd(env.src, ord); @@ -374,5 +385,7 @@ CuSuite *get_give_suite(void) SUITE_ADD_TEST(suite, test_give_okay); SUITE_ADD_TEST(suite, test_give_denied_by_rules); SUITE_ADD_TEST(suite, test_give_invalid_target); + SUITE_ADD_TEST(suite, test_give_new_unit); + SUITE_ADD_TEST(suite, test_give_dead_unit); return suite; } diff --git a/src/gmtool.c b/src/gmtool.c index 3c50aaf45..7a87e4ae6 100644 --- a/src/gmtool.c +++ b/src/gmtool.c @@ -51,6 +51,7 @@ #include #include +#include #include #include #include @@ -77,6 +78,62 @@ state *current_state = NULL; static WINDOW *hstatus; +#ifdef STDIO_CP +int gm_codepage = STDIO_CP; +#else +int gm_codepage = -1; +#endif + +static void unicode_remove_diacritics(const char *rp, char *wp) { + while (*rp) { + if (gm_codepage >=0 && *rp & 0x80) { + size_t sz = 0; + char ch; + switch (gm_codepage) { + case 1252: + unicode_utf8_to_cp1252(&ch, rp, &sz); + break; + case 437: + unicode_utf8_to_cp437(&ch, rp, &sz); + break; + default: + unicode_utf8_to_ascii(&ch, rp, &sz); + break; + } + rp += sz; + *wp++ = ch; + } + else { + *wp++ = *rp++; + } + } + *wp = 0; +} + +static void simplify(const char *rp, char *wp) { + unicode_remove_diacritics(rp, wp); +} + +int umvwprintw(WINDOW *win, int y, int x, const char *format, ...) { + char buffer[128]; + va_list args; + + va_start(args, format); + memset(buffer, 0, sizeof(buffer)); + vsnprintf(buffer, sizeof(buffer)-1, format, args); + va_end(args); + + simplify(buffer, buffer); + + return mvwaddstr(win, y, x, buffer); +} + +int umvwaddnstr(WINDOW *w, int y, int x, const char * str, int len) { + char buffer[128]; + simplify(str, buffer); + return mvwaddnstr(w, y, x, buffer, len); +} + static void init_curses(void) { int fg, bg; @@ -354,7 +411,7 @@ static void paint_status(window * wnd, const state * st) terrain = mr->r->terrain->_name; } cnormalize(&st->cursor, &nx, &ny); - mvwprintw(win, 0, 0, "%4d %4d | %.4s | %.20s (%d)", nx, ny, terrain, name, + umvwprintw(win, 0, 0, "%4d %4d | %.4s | %.20s (%d)", nx, ny, terrain, name, uid); wclrtoeol(win); } @@ -377,13 +434,13 @@ static void paint_info_region(window * wnd, const state * st) if (mr && mr->r) { const region *r = mr->r; if (r->land) { - mvwaddnstr(win, line++, 1, (char *)r->land->name, size); + umvwaddnstr(win, line++, 1, (char *)r->land->name, size); } else { - mvwaddnstr(win, line++, 1, r->terrain->_name, size); + umvwaddnstr(win, line++, 1, r->terrain->_name, size); } line++; - mvwprintw(win, line++, 1, "%s, age %d", r->terrain->_name, r->age); + umvwprintw(win, line++, 1, "%s, age %d", r->terrain->_name, r->age); if (r->land) { mvwprintw(win, line++, 1, "$:%6d P:%5d", rmoney(r), rpeasants(r)); mvwprintw(win, line++, 1, "H:%6d %s:%5d", rhorses(r), @@ -398,7 +455,7 @@ static void paint_info_region(window * wnd, const state * st) wattroff(win, A_BOLD | COLOR_PAIR(COLOR_YELLOW)); for (sh = r->ships; sh && line < maxline; sh = sh->next) { mvwprintw(win, line, 1, "%.4s ", itoa36(sh->no)); - mvwaddnstr(win, line++, 6, (char *)sh->type->_name, size - 5); + umvwaddnstr(win, line++, 6, (char *)sh->type->_name, size - 5); } } if (r->units && (st->info_flags & IFL_FACTIONS)) { @@ -409,7 +466,7 @@ static void paint_info_region(window * wnd, const state * st) for (u = r->units; u && line < maxline; u = u->next) { if (!fval(u->faction, FFL_MARK)) { mvwprintw(win, line, 1, "%.4s ", itoa36(u->faction->no)); - mvwaddnstr(win, line++, 6, (char *)u->faction->name, size - 5); + umvwaddnstr(win, line++, 6, (char *)u->faction->name, size - 5); fset(u->faction, FFL_MARK); } } @@ -424,7 +481,7 @@ static void paint_info_region(window * wnd, const state * st) wattroff(win, A_BOLD | COLOR_PAIR(COLOR_YELLOW)); for (u = r->units; u && line < maxline; u = u->next) { mvwprintw(win, line, 1, "%.4s ", itoa36(u->no)); - mvwaddnstr(win, line++, 6, unit_getname(u), size - 5); + umvwaddnstr(win, line++, 6, unit_getname(u), size - 5); } } } @@ -848,7 +905,7 @@ static void handlekey(state * st, int c) break; case 'B': if (!new_players) { - sprintf(sbuffer, "%s/newfactions", basepath()); + join_path(basepath(), "newfactions", sbuffer, sizeof(sbuffer)); new_players = read_newfactions(sbuffer); } cnormalize(&st->cursor, &nx, &ny); @@ -1063,7 +1120,7 @@ static void handlekey(state * st, int c) break; case 'A': if (!new_players) { - sprintf(sbuffer, "%s/newfactions", basepath()); + join_path(basepath(), "newfactions", sbuffer, sizeof(sbuffer)); new_players = read_newfactions(sbuffer); } seed_players(&new_players, false); @@ -1236,11 +1293,13 @@ void run_mapper(void) WINDOW *hwininfo; WINDOW *hwinmap; int width, height, x, y; - int split = 20, old_flags = log_flags; + int split = 20; state *st; point tl; - +/* FIXME: dsiable logging + int old_flags = log_flags; log_flags &= ~(LOG_CPERROR | LOG_CPWARNING); +*/ init_curses(); curs_set(1); @@ -1332,7 +1391,9 @@ void run_mapper(void) set_readline(NULL); curs_set(1); endwin(); +/* FIXME: reset logging log_flags = old_flags; +*/ state_close(st); } diff --git a/src/gmtool.h b/src/gmtool.h index d5396a615..3a682f453 100644 --- a/src/gmtool.h +++ b/src/gmtool.h @@ -31,6 +31,7 @@ extern "C" { void run_mapper(void); extern int force_color; + extern int gm_codepage; struct state *state_open(void); void state_close(struct state *); diff --git a/src/guard.c b/src/guard.c index 9e3585a41..f779e7e2e 100644 --- a/src/guard.c +++ b/src/guard.c @@ -38,6 +38,7 @@ attrib_type at_guard = { DEFAULT_AGE, a_writeint, a_readint, + NULL, ATF_UNIQUE }; diff --git a/src/items/xerewards.c b/src/items/xerewards.c index 1868d3a67..98e099070 100644 --- a/src/items/xerewards.c +++ b/src/items/xerewards.c @@ -21,6 +21,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "xerewards.h" #include "magic.h" +#include "study.h" /* kernel includes */ #include @@ -52,9 +53,7 @@ struct order *ord) for (n = 0; n != amount; ++n) { skill *sv = u->skills; while (sv != u->skills + u->skill_size) { - int i; - for (i = 0; i != 3; ++i) - learn_skill(u, (skill_t)sv->id, 1.0); + learn_skill(u, (skill_t)sv->id, STUDYDAYS * 3); ++sv; } } diff --git a/src/items/xerewards.test.c b/src/items/xerewards.test.c index f2f6e50ff..ea9f53fde 100644 --- a/src/items/xerewards.test.c +++ b/src/items/xerewards.test.c @@ -1,6 +1,7 @@ #include #include "xerewards.h" +#include "study.h" #include #include @@ -30,17 +31,17 @@ static void test_skillpotion(CuTest *tc) { itype = test_create_itemtype("skillpotion"); change_resource(u, itype->rtype, 2); - learn_skill(u, SK_ENTERTAINMENT, 1.0); + learn_skill(u, SK_ENTERTAINMENT, STUDYDAYS); pSkill = unit_skill(u, SK_ENTERTAINMENT); sk_set(pSkill, 5); initialWeeks_Entertainment = pSkill->weeks = 4; - learn_skill(u, SK_STAMINA, 1.0); + learn_skill(u, SK_STAMINA, STUDYDAYS); pSkill = unit_skill(u, SK_STAMINA); sk_set(pSkill, 5); initialWeeks_Stamina = pSkill->weeks = 4; - learn_skill(u, SK_MAGIC, 1.0); + learn_skill(u, SK_MAGIC, STUDYDAYS); pSkill = unit_skill(u, SK_MAGIC); sk_set(pSkill, 5); initialWeeks_Magic = pSkill->weeks = 4; diff --git a/src/kernel/CMakeLists.txt b/src/kernel/CMakeLists.txt index fd8496f89..9e829d4f4 100644 --- a/src/kernel/CMakeLists.txt +++ b/src/kernel/CMakeLists.txt @@ -2,26 +2,34 @@ project(kernel C) SET(_TEST_FILES alliance.test.c -build.test.c -config.test.c -group.test.c -faction.test.c -unit.test.c -save.test.c -ship.test.c -spell.test.c ally.test.c +build.test.c building.test.c -equipment.test.c +command.test.c +config.test.c +# connection.test.c curse.test.c +equipment.test.c +faction.test.c +group.test.c item.test.c +messages.test.c order.test.c +# pathdinder.test.c +plane.test.c pool.test.c race.test.c +# region.test.c +# resources.test.c +save.test.c +ship.test.c +# skills.test.c spellbook.test.c -curse.test.c +spell.test.c +# terrain.test.c +unit.test.c jsonconf.test.c -messages.test.c +# xmlreader.test.c ) SET(_FILES diff --git a/src/kernel/alliance.c b/src/kernel/alliance.c index 1de816731..c9bd26f40 100644 --- a/src/kernel/alliance.c +++ b/src/kernel/alliance.c @@ -58,7 +58,7 @@ void free_alliances(void) alliance *makealliance(int id, const char *name) { - alliance *al;; + alliance *al; for (;;) { if (id > 0) { @@ -73,6 +73,13 @@ alliance *makealliance(int id, const char *name) } id = id ? id : (1 + (rng_int() % MAX_UNIT_NR)); } + return new_alliance(id, name); +} + +alliance *new_alliance(int id, const char *name) { + alliance *al; + assert(id>0); + al = calloc(1, sizeof(alliance)); al->id = id; if (name) { @@ -277,9 +284,25 @@ static void perform_join(void) } } -static void execute(const struct syntaxtree *syntax, keyword_t kwd) + +static syntaxtree * build_syntax(void) { + syntaxtree *slang, *stree = stree_create(); + for (slang = stree; slang; slang = slang->next) { + void *leaf = 0; + add_command(&leaf, NULL, LOC(slang->lang, alliance_kwd[ALLIANCE_KICK]), &cmd_kick); + add_command(&leaf, NULL, LOC(slang->lang, alliance_kwd[ALLIANCE_LEAVE]), &cmd_leave); + add_command(&leaf, NULL, LOC(slang->lang, alliance_kwd[ALLIANCE_TRANSFER]), &cmd_transfer); + add_command(&leaf, NULL, LOC(slang->lang, alliance_kwd[ALLIANCE_NEW]), &cmd_new); + add_command(&leaf, NULL, LOC(slang->lang, alliance_kwd[ALLIANCE_INVITE]), &cmd_invite); + add_command(&leaf, NULL, LOC(slang->lang, alliance_kwd[ALLIANCE_JOIN]), &cmd_join); + slang->root = leaf; + } + return stree; +} + +static void execute(keyword_t kwd) { - int run = 0; + struct syntaxtree *syntax = 0; region **rp = ®ions; while (*rp) { @@ -289,12 +312,13 @@ static void execute(const struct syntaxtree *syntax, keyword_t kwd) unit *u = *up; if (u->number) { const struct locale *lang = u->faction->locale; - void *root = stree_find(syntax, lang); order *ord; for (ord = u->orders; ord; ord = ord->next) { if (getkeyword(ord) == kwd) { + void *root; + if (!syntax) syntax = build_syntax(); + root = stree_find(syntax, lang); do_command(root, u, ord); - run = 1; } } } @@ -304,8 +328,8 @@ static void execute(const struct syntaxtree *syntax, keyword_t kwd) if (*rp == r) rp = &r->next; } - - if (run) { + if (syntax) { + stree_free(syntax); perform_kick(); perform_leave(); perform_transfer(); @@ -314,42 +338,21 @@ static void execute(const struct syntaxtree *syntax, keyword_t kwd) } } +const char* alliance_kwd[ALLIANCE_MAX] = { + "kick", + "leave", + "command", + "new", + "invite", + "join" +}; + void alliance_cmd(void) { - static syntaxtree *stree = NULL; - if (stree == NULL) { - syntaxtree *slang = stree = stree_create(); - while (slang) { - void *leaf = 0; - add_command(&leaf, NULL, LOC(slang->lang, "new"), &cmd_new); - add_command(&leaf, NULL, LOC(slang->lang, "invite"), &cmd_invite); - add_command(&leaf, NULL, LOC(slang->lang, "join"), &cmd_join); - add_command(&leaf, NULL, LOC(slang->lang, "kick"), &cmd_kick); - add_command(&leaf, NULL, LOC(slang->lang, "leave"), &cmd_leave); - add_command(&leaf, NULL, LOC(slang->lang, "command"), &cmd_transfer); - slang->root = leaf; - slang = slang->next; - } - } - execute(stree, K_ALLIANCE); + execute(K_ALLIANCE); /* some may have been kicked, must remove f->alliance==NULL */ } -void alliancejoin(void) -{ - static syntaxtree *stree = NULL; - if (stree == NULL) { - syntaxtree *slang = stree = stree_create(); - while (slang) { - void *leaf = 0; - add_command(&leaf, NULL, LOC(slang->lang, "join"), &cmd_join); - add_command(&slang->root, leaf, LOC(slang->lang, "alliance"), NULL); - slang = slang->next; - } - } - execute(stree, K_ALLIANCE); -} - void setalliance(faction * f, alliance * al) { if (f->alliance == al) @@ -442,82 +445,6 @@ void alliancevictory(void) } } -int victorycondition(const alliance * al, const char *name) -{ - const char *gems[] = - { "opal", "diamond", "zaphire", "topaz", "beryl", "agate", "garnet", - "emerald", NULL }; - if (strcmp(name, "gems") == 0) { - const char **igem; - - for (igem = gems; *igem; ++igem) { - const struct resource_type *rtype = rt_find(*igem); - quicklist *flist = al->members; - int qi; - bool found = false; - - assert(rtype); - for (qi = 0; flist && !found; ql_advance(&flist, &qi, 1)) { - faction *f = (faction *)ql_get(flist, 0); - unit *u; - - for (u = f->units; u; u = u->nextF) { - if (i_get(u->items, rtype->itype) > 0) { - found = true; - break; - } - } - } - if (!found) - return 0; - } - return 1; - - } - else if (strcmp(name, "phoenix") == 0) { - quicklist *flist = al->members; - int qi; - - for (qi = 0; flist; ql_advance(&flist, &qi, 1)) { - faction *f = (faction *)ql_get(flist, qi); - if (find_key(f->attribs, atoi36("phnx"))) { - return 1; - } - } - return 0; - - } - else if (strcmp(name, "pyramid") == 0) { - - /* Logik: - * - if (pyr > last_passed_size && pyr > all_others) { - * pyr->passed->counter++; - * for(all_other_pyrs) { - * pyr->passed->counter=0; - * } - * - * if(pyr->passed->counter >= 3) { - * set(pyr, passed); - * pyr->owner->set_attrib(pyra); - * } - * last_passed_size = pyr->size; - * } - */ - - quicklist *flist = al->members; - int qi; - - for (qi = 0; flist; ql_advance(&flist, &qi, 1)) { - faction *f = (faction *)ql_get(flist, qi); - if (find_key(f->attribs, atoi36("pyra"))) { - return 1; - } - } - return 0; - } - return -1; -} - void alliance_setname(alliance * self, const char *name) { free(self->name); diff --git a/src/kernel/alliance.h b/src/kernel/alliance.h index b96342397..907fc65f7 100644 --- a/src/kernel/alliance.h +++ b/src/kernel/alliance.h @@ -38,6 +38,7 @@ extern "C" { ALLIANCE_MAX }; + extern const char* alliance_kwd[ALLIANCE_MAX]; #define ALF_NON_ALLIED (1<<0) /* this alliance is just a default for a non-allied faction */ #define ALLY_ENEMY (1<<0) @@ -53,13 +54,14 @@ extern "C" { } alliance; extern alliance *alliances; - extern alliance *findalliance(int id); - extern alliance *makealliance(int id, const char *name); - extern const char *alliancename(const struct alliance *al); - extern void setalliance(struct faction *f, alliance * al); + alliance *findalliance(int id); + alliance *new_alliance(int id, const char *name); + alliance *makealliance(int id, const char *name); + const char *alliancename(const struct alliance *al); + void setalliance(struct faction *f, alliance * al); void free_alliances(void); - extern struct faction *alliance_get_leader(struct alliance *al); - extern void alliance_cmd(void); + struct faction *alliance_get_leader(struct alliance *al); + void alliance_cmd(void); bool is_allied(const struct faction *f1, const struct faction *f2); void alliance_setname(alliance * self, const char *name); diff --git a/src/kernel/alliance.test.c b/src/kernel/alliance.test.c index 1b708b017..8432f1692 100644 --- a/src/kernel/alliance.test.c +++ b/src/kernel/alliance.test.c @@ -1,6 +1,9 @@ #include #include #include +#include +#include +#include #include "alliance.h" #include #include @@ -83,12 +86,126 @@ static void test_alliance_dead_faction(CuTest *tc) { test_cleanup(); } +static void test_alliance_cmd(CuTest *tc) { + unit *u1, *u2; + struct region *r; + struct alliance *al; + + test_cleanup(); + 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); + 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, u1->faction)); + CuAssertTrue(tc, !is_allied(u1->faction, u2->faction)); + CuAssertPtrEquals(tc, 0, f_get_alliance(u1->faction)); + alliance_cmd(); + al = f_get_alliance(u1->faction); + CuAssertPtrNotNull(tc, al); + CuAssertIntEquals(tc, 42, al->id); + CuAssertPtrNotNull(tc, al->members); + CuAssertPtrEquals(tc, u1->faction, alliance_get_leader(al)); + CuAssertPtrEquals(tc, al, findalliance(42)); + CuAssertTrue(tc, is_allied(u1->faction, u1->faction)); + CuAssertPtrEquals(tc, al, u2->faction->alliance); + test_cleanup(); +} + +static void test_alliance_cmd_kick(CuTest *tc) { + unit *u1, *u2; + struct region *r; + struct alliance *al; + + test_cleanup(); + al = makealliance(42, "Hodor"); + 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); + setalliance(u1->faction, al); + setalliance(u2->faction, al); + + unit_addorder(u1, create_order(K_ALLIANCE, u1->faction->locale, "%s %s", alliance_kwd[ALLIANCE_KICK], itoa36(u2->faction->no))); + CuAssertTrue(tc, is_allied(u1->faction, u2->faction)); + alliance_cmd(); + CuAssertTrue(tc, !is_allied(u1->faction, u2->faction)); + CuAssertPtrEquals(tc, 0, f_get_alliance(u2->faction)); + test_cleanup(); +} + +static void test_alliance_cmd_no_invite(CuTest *tc) { + unit *u1, *u2; + struct region *r; + + test_cleanup(); + 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); + unit_addorder(u1, create_order(K_ALLIANCE, u1->faction->locale, "%s %s", alliance_kwd[ALLIANCE_NEW], itoa36(42))); + unit_addorder(u2, create_order(K_ALLIANCE, u1->faction->locale, "%s %s", alliance_kwd[ALLIANCE_JOIN], itoa36(42))); + CuAssertTrue(tc, is_allied(u1->faction, u1->faction)); + 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)); + test_cleanup(); +} + +static void test_alliance_cmd_leave(CuTest *tc) { + unit *u1, *u2; + struct region *r; + struct alliance *al; + + test_cleanup(); + al = makealliance(42, "Hodor"); + 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); + setalliance(u1->faction, al); + setalliance(u2->faction, al); + + unit_addorder(u1, create_order(K_ALLIANCE, u1->faction->locale, "%s", alliance_kwd[ALLIANCE_LEAVE])); + CuAssertTrue(tc, is_allied(u1->faction, u2->faction)); + alliance_cmd(); + CuAssertTrue(tc, !is_allied(u1->faction, u2->faction)); + CuAssertPtrEquals(tc, 0, f_get_alliance(u1->faction)); + test_cleanup(); +} + +static void test_alliance_cmd_transfer(CuTest *tc) { + unit *u1, *u2; + struct region *r; + struct alliance *al; + + test_cleanup(); + al = makealliance(42, "Hodor"); + 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); + setalliance(u1->faction, al); + setalliance(u2->faction, al); + CuAssertPtrEquals(tc, u1->faction, alliance_get_leader(al)); + unit_addorder(u1, create_order(K_ALLIANCE, u1->faction->locale, "%s %s", alliance_kwd[ALLIANCE_TRANSFER], itoa36(u2->faction->no))); + alliance_cmd(); + CuAssertPtrEquals(tc, u2->faction, alliance_get_leader(al)); + test_cleanup(); +} + + CuSuite *get_alliance_suite(void) { CuSuite *suite = CuSuiteNew(); 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_cmd); + SUITE_ADD_TEST(suite, test_alliance_cmd_no_invite); + SUITE_ADD_TEST(suite, test_alliance_cmd_kick); + SUITE_ADD_TEST(suite, test_alliance_cmd_leave); + SUITE_ADD_TEST(suite, test_alliance_cmd_transfer); return suite; } diff --git a/src/kernel/ally.c b/src/kernel/ally.c index 611db8d7b..3152ab4b7 100644 --- a/src/kernel/ally.c +++ b/src/kernel/ally.c @@ -121,6 +121,7 @@ attrib_type at_npcfaction = { NULL, a_writeint, a_readint, + NULL, ATF_UNIQUE }; @@ -180,10 +181,10 @@ alliedgroup(const struct plane *pl, const struct faction *f, } mode = ally_mode(sf, mode) | (mode & autoalliance(pl, f, f2)); if (AllianceRestricted()) { - if (a_findc(f->attribs, &at_npcfaction)) { + if (a_find(f->attribs, &at_npcfaction)) { return mode; } - if (a_findc(f2->attribs, &at_npcfaction)) { + if (a_find(f2->attribs, &at_npcfaction)) { return mode; } if (f->alliance != f2->alliance) { @@ -232,7 +233,7 @@ int alliedunit(const unit * u, const faction * f2, int mode) sf = u->faction->allies; if (fval(u, UFL_GROUP)) { - const attrib *a = a_findc(u->attribs, &at_group); + const attrib *a = a_find(u->attribs, &at_group); if (a != NULL) sf = ((group *)a->data.v)->allies; } diff --git a/src/kernel/build.c b/src/kernel/build.c index 98ca2b28e..f9e10b09e 100644 --- a/src/kernel/build.c +++ b/src/kernel/build.c @@ -23,6 +23,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "alchemy.h" #include "direction.h" #include "move.h" +#include "study.h" #include "laws.h" #include "skill.h" #include "lighthouse.h" @@ -513,7 +514,7 @@ 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 > 1) { + if (type->maxsize > 0) { completed = completed % type->maxsize; } else { @@ -804,7 +805,7 @@ build_building(unit * u, const building_type * btype, int id, int want, order * /* build a new building */ b = new_building(btype, r, lang); b->type = btype; - fset(b, BLD_MAINTAINED | BLD_WORKING); + fset(b, BLD_MAINTAINED); /* Die Einheit befindet sich automatisch im Inneren der neuen Burg. */ if (u->number && leave(u, false)) { @@ -847,6 +848,9 @@ build_building(unit * u, const building_type * btype, int id, int want, order * } b->size += built; + if (b->type->maxsize > 0 && b->size > b->type->maxsize) { + log_error("build: %s has size=%d, maxsize=%d", buildingname(b), b->size, b->type->maxsize); + } fset(b, BLD_EXPANDED); update_lighthouse(b); diff --git a/src/kernel/build.test.c b/src/kernel/build.test.c index 8e26d86c5..41dcdb1a5 100644 --- a/src/kernel/build.test.c +++ b/src/kernel/build.test.c @@ -34,7 +34,6 @@ static unit * setup_build(build_fixture *bf) { bf->rc = test_create_race("human"); bf->r = test_create_region(0, 0, 0); bf->f = test_create_faction(bf->rc); - bf->f->locale = get_or_create_locale("de"); assert(bf->rc && bf->f && bf->r); bf->u = test_create_unit(bf->f, bf->r); assert(bf->u); @@ -84,7 +83,7 @@ static void test_build_requires_building(CuTest *tc) { 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_WORKING); + 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)); diff --git a/src/kernel/building.c b/src/kernel/building.c index 4384b7f80..cf40c0ea3 100644 --- a/src/kernel/building.c +++ b/src/kernel/building.c @@ -23,7 +23,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* kernel includes */ #include "item.h" -#include "curse.h" /* für C_NOCOST */ #include "unit.h" #include "faction.h" #include "race.h" @@ -39,6 +38,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include #include #include @@ -132,7 +132,7 @@ int buildingcapacity(const building * b) } return b->size * b->type->capacity; } - if (b->size >= b->type->maxsize) { + if (building_finished(b)) { if (b->type->maxcapacity >= 0) { return b->type->maxcapacity; } @@ -141,7 +141,7 @@ int buildingcapacity(const building * b) } attrib_type at_building_generic_type = { - "building_generic_type", NULL, NULL, NULL, a_writestring, a_readstring, + "building_generic_type", NULL, NULL, NULL, a_writestring, a_readstring, NULL, ATF_UNIQUE }; @@ -386,10 +386,10 @@ int resolve_building(variant id, void *address) return result; } -variant read_building_reference(struct storage * store) +variant read_building_reference(gamedata * data) { variant result; - READ_INT(store, &result.i); + READ_INT(data->store, &result.i); return result; } @@ -648,15 +648,20 @@ buildingtype_exists(const region * r, const building_type * bt, bool working) building *b; for (b = rbuildings(r); b; b = b->next) { - if (b->type == bt && b->size >= bt->maxsize && (!working || fval(b, BLD_WORKING))) + if (b->type == bt && (!working || fval(b, BLD_MAINTAINED)) && building_finished(b)) { return true; + } } return false; } +bool building_finished(const struct building *b) { + return b->size >= b->type->maxsize; +} + bool building_is_active(const struct building *b) { - return b && fval(b, BLD_WORKING) && b->size >= b->type->maxsize; + return b && fval(b, BLD_MAINTAINED) && building_finished(b); } building *active_building(const unit *u, const struct building_type *btype) { diff --git a/src/kernel/building.h b/src/kernel/building.h index 4d08ae647..fda4a4676 100644 --- a/src/kernel/building.h +++ b/src/kernel/building.h @@ -26,6 +26,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. extern "C" { #endif + struct gamedata; + /* maintenance::flags */ #define MTF_NONE 0x00 #define MTF_VARIABLE 0x01 /* resource usage scales with size */ @@ -90,22 +92,13 @@ extern "C" { const struct building *b, int bsize); bool in_safe_building(struct unit *u1, struct unit *u2); - /* buildingt => building_type - * Name => locale_string(name) - * MaxGroesse => levels - * MinBauTalent => construction->minskill - * Kapazitaet => capacity, maxcapacity - * Materialien => construction->materials - * UnterSilber, UnterSpezialTyp, UnterSpezial => maintenance - * per_size => !maintenance->fixed - */ + #define BFL_NONE 0x00 #define BLD_MAINTAINED 0x01 /* vital maintenance paid for */ -#define BLD_WORKING 0x02 /* full maintenance paid, it works */ +#define BLD_DONTPAY 0x02 /* PAY NOT */ #define BLD_UNGUARDED 0x04 /* you can enter this building anytime */ #define BLD_EXPANDED 0x08 /* has been expanded this turn */ #define BLD_SELECT 0x10 /* formerly FL_DH */ -#define BLD_DONTPAY 0x20 /* PAY NOT */ #define BLD_SAVEMASK 0x00 /* mask for persistent flags */ @@ -136,6 +129,7 @@ extern "C" { struct region *r, const struct locale *lang); int build_building(struct unit *u, const struct building_type *typ, int id, int size, struct order *ord); + bool building_finished(const struct building *b); /* Alte Gebäudetypen: */ @@ -158,7 +152,7 @@ extern "C" { extern int resolve_building(variant data, void *address); extern void write_building_reference(const struct building *b, struct storage *store); - extern variant read_building_reference(struct storage *store); + extern variant read_building_reference(struct gamedata *data); extern struct building *findbuilding(int n); @@ -171,10 +165,6 @@ extern "C" { bool building_is_active(const struct building *b); struct building *active_building(const struct unit *u, const struct building_type *btype); -#ifdef WDW_PYRAMID - extern int wdw_pyramid_level(const struct building *b); -#endif - extern const char *buildingname(const struct building *b); extern const char *building_getname(const struct building *b); diff --git a/src/kernel/building.test.c b/src/kernel/building.test.c index c0c355940..1c5ff1fe2 100644 --- a/src/kernel/building.test.c +++ b/src/kernel/building.test.c @@ -409,17 +409,17 @@ static void test_buildingtype_exists(CuTest * tc) CuAssertTrue(tc, buildingtype_exists(r, btype, false)); b->size = 9; - fset(b, BLD_WORKING); + fset(b, BLD_MAINTAINED); CuAssertTrue(tc, !buildingtype_exists(r, btype, false)); btype->maxsize = 0; - freset(b, BLD_WORKING); + freset(b, BLD_MAINTAINED); CuAssertTrue(tc, buildingtype_exists(r, btype, false)); btype->maxsize = 10; b->size = 10; - fset(b, BLD_WORKING); + fset(b, BLD_MAINTAINED); CuAssertTrue(tc, buildingtype_exists(r, btype, true)); - freset(b, BLD_WORKING); + freset(b, BLD_MAINTAINED); CuAssertTrue(tc, !buildingtype_exists(r, btype, true)); } @@ -438,7 +438,7 @@ static void test_active_building(CuTest *tc) { CuAssertIntEquals(tc, false, building_is_active(b)); CuAssertPtrEquals(tc, NULL, active_building(u, btype)); - b->flags |= BLD_WORKING; + b->flags |= BLD_MAINTAINED; CuAssertIntEquals(tc, true, building_is_active(b)); CuAssertPtrEquals(tc, NULL, active_building(u, btype)); u_set_building(u, b); @@ -452,7 +452,7 @@ static void test_active_building(CuTest *tc) { CuAssertIntEquals(tc, false, building_is_active(b)); CuAssertPtrEquals(tc, NULL, active_building(u, btype)); btype->maxsize = -1; - b->flags &= ~BLD_WORKING; + b->flags &= ~BLD_MAINTAINED; CuAssertIntEquals(tc, false, building_is_active(b)); CuAssertPtrEquals(tc, NULL, active_building(u, btype)); test_cleanup(); diff --git a/src/kernel/command.c b/src/kernel/command.c index ac359a4cf..0642be060 100644 --- a/src/kernel/command.c +++ b/src/kernel/command.c @@ -43,6 +43,15 @@ void *stree_find(const syntaxtree * stree, const struct locale *lang) return NULL; } +void stree_free(syntaxtree *stree) { + while (stree) { + syntaxtree *snext = stree->next; + freetokens(stree->root); + free(stree); + stree = snext; + } +} + syntaxtree *stree_create(void) { syntaxtree *sroot = NULL; @@ -65,6 +74,7 @@ const char *str, parser fun) command *cmd = (command *)malloc(sizeof(command)); variant var; + assert(str); cmd->fun = fun; cmd->nodes = tnext; var.v = cmd; diff --git a/src/kernel/command.h b/src/kernel/command.h index 54d8fb25c..70b03a3a6 100644 --- a/src/kernel/command.h +++ b/src/kernel/command.h @@ -32,6 +32,7 @@ extern "C" { void do_command(const void *troot, struct unit *u, struct order *); struct syntaxtree *stree_create(void); + void stree_free(struct syntaxtree *); void *stree_find(const struct syntaxtree *stree, const struct locale *lang); diff --git a/src/kernel/command.test.c b/src/kernel/command.test.c new file mode 100644 index 000000000..edf521c81 --- /dev/null +++ b/src/kernel/command.test.c @@ -0,0 +1,57 @@ +/* + +-------------------+ Christian Schlittchen + | | Enno Rehling + | Eressea PBEM host | Katja Zedel + | (c) 1998 - 2003 | Henning Peters + | | Ingo Wilken + +-------------------+ Stefan Reich + + This program may not be used, modified or distributed + without prior permission by the authors of Eressea. + + */ +#include "command.h" +#include "unit.h" +#include "order.h" + +#include +#include + +static void parser_two(const void *nodes, struct unit * u, struct order *ord) { + scale_number(u, 2); +} + +static void parser_six(const void *nodes, struct unit * u, struct order *ord) { + scale_number(u, 6); +} + +static void test_command(CuTest * tc) { + struct syntaxtree *st; + struct locale * loc; + unit *u; + + test_cleanup(); + loc = test_create_locale(); + st = stree_create(); + CuAssertPtrNotNull(tc, st); + CuAssertPtrEquals(tc, loc, (struct locale *)st->lang); + CuAssertPtrEquals(tc, 0, st->root); + CuAssertPtrEquals(tc, 0, st->next); + add_command(&st->root, 0, "two", parser_two); + add_command(&st->root, 0, "six", parser_six); + CuAssertPtrNotNull(tc, st->root); + CuAssertPtrEquals(tc, st->root, stree_find(st, loc)); + u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); + u->thisorder = create_order(K_ALLIANCE, loc, "two"); + do_command(st->root, u, u->thisorder); + CuAssertIntEquals(tc, u->number, 2); + stree_free(st); + test_cleanup(); +} + +CuSuite *get_command_suite(void) +{ + CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_command); + return suite; +} \ No newline at end of file diff --git a/src/kernel/config.c b/src/kernel/config.c index 08b7f53c9..cda449a23 100644 --- a/src/kernel/config.c +++ b/src/kernel/config.c @@ -659,11 +659,16 @@ void set_basepath(const char *path) #define PATH_DELIM '/' #endif - char * join_path(const char *p1, const char *p2, char *dst, size_t len) { size_t sz; assert(p1 && p2); - sz = strlcpy(dst, p1, len); + assert(p2 != dst); + if (dst == p1) { + sz = strlen(p1); + } + else { + sz = strlcpy(dst, p1, len); + } assert(sz < len); dst[sz++] = PATH_DELIM; strlcpy(dst + sz, p2, len - sz); @@ -998,33 +1003,24 @@ void kernel_init(void) } static order * defaults[MAXLOCALES]; -keyword_t default_keyword = NOKEYWORD; -void set_default_order(int kwd) { - default_keyword = (keyword_t)kwd; -} - -// TODO: outside of tests, default_keyword is never used, why is this here? -// see also test_long_order_hungry order *default_order(const struct locale *lang) { - static int usedefault = 1; int i = locale_index(lang); order *result = 0; assert(i < MAXLOCALES); - if (default_keyword != NOKEYWORD) { - return create_order(default_keyword, lang, 0); - } - result = defaults[i]; - if (!result && usedefault) { - const char * str = LOC(lang, "defaultorder"); + if (!result) { + const char * str; + keyword_t kwd = NOKEYWORD; + str = config_get("orders.default"); if (str) { - result = defaults[i] = parse_order(str, lang); + kwd = findkeyword(str); } - else { - usedefault = 0; + if (kwd != NOKEYWORD) { + result = create_order(kwd, lang, NULL); + defaults[i] = result; } } return result ? copy_order(result) : 0; @@ -1054,7 +1050,7 @@ int rule_give(void) bool markets_module(void) { - return config_get_int("modules.markets", 0); + return (bool)config_get_int("modules.markets", 0); } static struct param *configuration; @@ -1080,7 +1076,6 @@ bool config_token(const char *key, const char *tok) { } void free_config(void) { - global.functions.maintenance = NULL; global.functions.wage = NULL; free_params(&configuration); } @@ -1109,8 +1104,7 @@ void free_gamedata(void) while (planes) { plane *pl = planes; planes = planes->next; - free(pl->name); - free(pl); + free_plane(pl); } while (global.attribs) { diff --git a/src/kernel/config.h b/src/kernel/config.h index dda7873aa..081a96e61 100644 --- a/src/kernel/config.h +++ b/src/kernel/config.h @@ -161,11 +161,9 @@ struct param; void *vm_state; int data_version; /* TODO: eliminate in favor of gamedata.version */ struct _dictionary_ *inifile; - struct global_functions { int(*wage) (const struct region * r, const struct faction * f, const struct race * rc, int in_turn); - int(*maintenance) (const struct unit * u); } functions; } settings; @@ -195,7 +193,6 @@ struct param; bool IsImmune(const struct faction *f); struct order *default_order(const struct locale *lang); - void set_default_order(int kwd); int entertainmoney(const struct region *r); void init_parameters(struct locale *lang); diff --git a/src/kernel/config.test.c b/src/kernel/config.test.c index 7078d17ac..d5b46581c 100644 --- a/src/kernel/config.test.c +++ b/src/kernel/config.test.c @@ -22,8 +22,7 @@ static void test_read_unitid(CuTest *tc) { struct terrain_type *t_plain; test_cleanup(); - lang = get_or_create_locale("de"); - test_translate_param(lang, P_TEMP, "TEMP"); + lang = test_create_locale(); /* note that the english order is FIGHT, not COMBAT, so this is a poor example */ t_plain = test_create_terrain("plain", LAND_REGION); u = test_create_unit(test_create_faction(0), test_create_region(0, 0, t_plain)); @@ -68,8 +67,7 @@ static void test_getunit(CuTest *tc) { struct terrain_type *t_plain; test_cleanup(); - lang = get_or_create_locale("de"); - test_translate_param(lang, P_TEMP, "TEMP"); + lang = test_create_locale(); /* note that the english order is FIGHT, not COMBAT, so this is a poor example */ t_plain = test_create_terrain("plain", LAND_REGION); u = test_create_unit(test_create_faction(0), test_create_region(0, 0, t_plain)); @@ -174,6 +172,23 @@ static void test_forbiddenid(CuTest *tc) { CuAssertIntEquals(tc, 1, forbiddenid(atoi36("t"))); } +static void test_default_order(CuTest *tc) { + order *ord; + struct locale * loc; + + test_cleanup(); + loc = test_create_locale(); + ord = default_order(loc); + CuAssertPtrEquals(tc, 0, ord); + + config_set("orders.default", "work"); + ord = default_order(loc); + CuAssertPtrNotNull(tc, ord); + CuAssertIntEquals(tc, K_WORK, getkeyword(ord)); + CuAssertPtrEquals(tc, ord->data, default_order(loc)->data); + test_cleanup(); +} + CuSuite *get_config_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -183,5 +198,6 @@ CuSuite *get_config_suite(void) SUITE_ADD_TEST(suite, test_forbiddenid); SUITE_ADD_TEST(suite, test_getunit); SUITE_ADD_TEST(suite, test_read_unitid); + SUITE_ADD_TEST(suite, test_default_order); return suite; } diff --git a/src/kernel/connection.c b/src/kernel/connection.c index dbf29bff9..ef86f9a3a 100644 --- a/src/kernel/connection.c +++ b/src/kernel/connection.c @@ -28,6 +28,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include +#include #include #include #include @@ -217,8 +218,9 @@ border_type *find_bordertype(const char *name) return bt; } -void b_read(connection * b, storage * store) +void b_read(connection * b, gamedata * data) { + storage * store = data->store; int n, result = 0; switch (b->type->datatype) { case VAR_NONE: @@ -529,8 +531,9 @@ static const char *b_nameroad(const connection * b, const region * r, return buffer; } -static void b_readroad(connection * b, storage * store) +static void b_readroad(connection * b, gamedata * data) { + storage * store = data->store; int n; READ_INT(store, &n); b->data.sa[0] = (short)n; @@ -601,8 +604,9 @@ void write_borders(struct storage *store) WRITE_TOK(store, "end"); } -int read_borders(struct storage *store) +int read_borders(gamedata *data) { + struct storage *store = data->store; for (;;) { int bid = 0; char zText[32]; @@ -612,8 +616,15 @@ int read_borders(struct storage *store) READ_TOK(store, zText, sizeof(zText)); if (!strcmp(zText, "end")) break; + type = find_bordertype(zText); + if (type == NULL) { + log_error("[read_borders] connection %d type %s is not registered", bid, zText); + assert(type || !"connection type not registered"); + } + + READ_INT(store, &bid); - if (global.data_version < UIDHASH_VERSION) { + if (data->version < UIDHASH_VERSION) { int fx, fy, tx, ty; READ_INT(store, &fx); READ_INT(store, &fy); @@ -628,16 +639,17 @@ int read_borders(struct storage *store) READ_INT(store, &tid); from = findregionbyid(fid); to = findregionbyid(tid); - if (!to || !from) { - log_warning("%s connection between incomplete regions %d and %d", zText, fid, tid); - continue; - } } - - type = find_bordertype(zText); - if (type == NULL) { - log_error("[read_borders] unknown connection type '%s' in %s\n", zText, regionname(from, NULL)); - assert(type || !"connection type not registered"); + if (!to || !from) { + if (!to || !from) { + log_error("%s connection %d has missing regions", zText, bid); + } + if (type->read) { + // skip ahead + connection dummy; + type->read(&dummy, data); + } + continue; } if (to == from && type && from) { @@ -647,19 +659,15 @@ int read_borders(struct storage *store) if (r != NULL) to = r; } - if ((type->read && !type->write)) { - log_warning("ignore invalid border '%s' between '%s' and '%s'\n", zText, regionname(from, 0), regionname(to, 0)); - } - else { + if (type->read) { connection *b = new_border(type, from, to); nextborder--; /* new_border erhöht den Wert */ b->id = bid; assert(bid <= nextborder); - if (type->read) - type->read(b, store); - if (global.data_version < NOBORDERATTRIBS_VERSION) { + type->read(b, data); + if (data->version < NOBORDERATTRIBS_VERSION) { attrib *a = NULL; - int result = a_read(store, &a, b); + int result = read_attribs(data, &a, b); if (border_convert_cb) { border_convert_cb(b, a); } @@ -670,7 +678,14 @@ int read_borders(struct storage *store) return result; } } + if (!type->write) { + log_warning("invalid border '%s' between '%s' and '%s'\n", zText, regionname(from, 0), regionname(to, 0)); + } } } return 0; } + +const char * border_name(const connection *co, const struct region * r, const struct faction * f, int flags) { + return (co->type->name) ? co->type->name(co, r, f, flags) : co->type->__name; +} diff --git a/src/kernel/connection.h b/src/kernel/connection.h index ed4e2a50e..7eef0f52a 100644 --- a/src/kernel/connection.h +++ b/src/kernel/connection.h @@ -30,6 +30,7 @@ extern "C" { struct faction; struct region; struct storage; + struct gamedata; struct unit; extern int nextborder; @@ -52,7 +53,7 @@ extern "C" { /* constructor: initialize the connection. allocate extra memory if needed */ void(*destroy) (connection *); /* destructor: remove all extra memory for destruction */ - void(*read) (connection *, struct storage *); + void(*read) (connection *, struct gamedata *); void(*write) (const connection *, struct storage *); bool(*block) (const connection *, const struct unit *, const struct region * r); @@ -113,12 +114,12 @@ extern "C" { void register_bordertype(border_type * type); /* register a new bordertype */ - int read_borders(struct storage *store); + int read_borders(struct gamedata *store); void write_borders(struct storage *store); void age_borders(void); /* provide default implementations for some member functions: */ - void b_read(connection * b, struct storage *store); + void b_read(connection * b, struct gamedata *store); void b_write(const connection * b, struct storage *store); bool b_blockall(const connection *, const struct unit *, const struct region *); @@ -147,6 +148,9 @@ extern "C" { /* game-specific callbacks */ void(*border_convert_cb) (struct connection * con, struct attrib * attr); + const char * border_name(const struct connection *co, + const struct region * r, const struct faction * f, int flags); + #ifdef __cplusplus } #endif diff --git a/src/kernel/curse.c b/src/kernel/curse.c index 3cb39ebab..612127182 100644 --- a/src/kernel/curse.c +++ b/src/kernel/curse.c @@ -35,6 +35,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* util includes */ #include #include +#include #include #include #include @@ -56,7 +57,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #define MAXENTITYHASH 7919 -curse *cursehash[MAXENTITYHASH]; +static curse *cursehash[MAXENTITYHASH]; void c_setflag(curse * c, unsigned int flags) { @@ -73,17 +74,19 @@ void c_clearflag(curse * c, unsigned int flags) void chash(curse * c) { - curse *old = cursehash[c->no % MAXENTITYHASH]; + int i = c->no % MAXENTITYHASH; - cursehash[c->no % MAXENTITYHASH] = c; - c->nexthash = old; + c->nexthash = cursehash[i]; + cursehash[i] = c; + assert(c->nexthash != c); } static void cunhash(curse * c) { curse **show; + int i = c->no % MAXENTITYHASH; - for (show = &cursehash[c->no % MAXENTITYHASH]; *show; + for (show = &cursehash[i]; *show; show = &(*show)->nexthash) { if ((*show)->no == c->no) break; @@ -95,16 +98,6 @@ static void cunhash(curse * c) } } -curse *cfindhash(int i) -{ - curse *old; - - for (old = cursehash[i % MAXENTITYHASH]; old; old = old->nexthash) - if (old->no == i) - return old; - return NULL; -} - /* ------------------------------------------------------------- */ /* at_curse */ void curse_init(attrib * a) @@ -184,8 +177,9 @@ static int read_ccompat(const char *cursename, struct storage *store) return -1; } -int curse_read(attrib * a, void *owner, struct storage *store) +int curse_read(attrib * a, void *owner, gamedata *data) { + storage *store = data->store; curse *c = (curse *)a->data.v; int ur; char cursename[64]; @@ -193,6 +187,7 @@ int curse_read(attrib * a, void *owner, struct storage *store) int flags; float flt; + assert(!c->no); READ_INT(store, &c->no); chash(c); READ_TOK(store, cursename, sizeof(cursename)); @@ -200,13 +195,13 @@ int curse_read(attrib * a, void *owner, struct storage *store) READ_INT(store, &c->duration); READ_FLT(store, &flt); c->vigour = flt; - if (global.data_version < INTPAK_VERSION) { - ur = read_reference(&c->magician, store, read_int, resolve_unit); + if (data->version < INTPAK_VERSION) { + ur = resolve_unit(read_int(data->store), &c->magician); } else { - ur = read_reference(&c->magician, store, read_unit_reference, resolve_unit); + ur = read_reference(&c->magician, data, read_unit_reference, resolve_unit); } - if (global.data_version < CURSEFLOAT_VERSION) { + if (data->version < CURSEFLOAT_VERSION) { READ_INT(store, &n); c->effect = (float)n; } @@ -224,19 +219,20 @@ int curse_read(attrib * a, void *owner, struct storage *store) return AT_READ_FAIL; } c->flags = flags; - if (global.data_version < EXPLICIT_CURSE_ISNEW_VERSION) { + if (data->version < EXPLICIT_CURSE_ISNEW_VERSION) { c_clearflag(c, CURSE_ISNEW); } - if (c->type->read) - c->type->read(store, c, owner); + if (c->type->read) { + c->type->read(data, c, owner); + } else if (c->type->typ == CURSETYP_UNIT) { READ_INT(store, &c->data.i); } if (c->type->typ == CURSETYP_REGION) { int rr = - read_reference(&c->data.v, store, read_region_reference, - RESOLVE_REGION(global.data_version)); + read_reference(&c->data.v, data, read_region_reference, + RESOLVE_REGION(data->version)); if (ur == 0 && rr == 0 && !c->data.v) { return AT_READ_FAIL; } @@ -253,12 +249,11 @@ void curse_write(const attrib * a, const void *owner, struct storage *store) unit *mage = (c->magician && c->magician->number) ? c->magician : NULL; /* copied from c_clearflag */ - if (global.data_version < EXPLICIT_CURSE_ISNEW_VERSION) { - flags = (c->flags & ~CURSE_ISNEW) | (c->type->flags & CURSE_ISNEW); - } - else { - flags = c->flags | c->type->flags; - } +#if RELEASE_VERSION < EXPLICIT_CURSE_ISNEW_VERSION + flags = (c->flags & ~CURSE_ISNEW) | (c->type->flags & CURSE_ISNEW); +#else + flags = c->flags | c->type->flags; +#endif WRITE_INT(store, c->no); WRITE_TOK(store, ct->cname); @@ -285,6 +280,7 @@ attrib_type at_curse = { curse_age, curse_write, curse_read, + NULL, ATF_CURSE }; @@ -363,6 +359,7 @@ static bool cmp_curse(const attrib * a, const void *data) curse *get_curse(attrib * ap, const curse_type * ctype) { attrib *a = ap; + if (!ctype) return NULL; while (a) { if (a->type->flags & ATF_CURSE) { const attrib_type *at = a->type; @@ -383,9 +380,13 @@ curse *get_curse(attrib * ap, const curse_type * ctype) /* ------------------------------------------------------------- */ /* findet einen curse global anhand seiner 'curse-Einheitnummer' */ -curse *findcurse(int cid) +curse *findcurse(int i) { - return cfindhash(cid); + curse *old; + for (old = cursehash[i % MAXENTITYHASH]; old; old = old->nexthash) + if (old->no == i) + return old; + return NULL; } /* ------------------------------------------------------------- */ @@ -694,32 +695,6 @@ bool is_cursed_with(const attrib * ap, const curse * c) /* ------------------------------------------------------------- */ /* cursedata */ /* ------------------------------------------------------------- */ -/* - * typedef struct curse_type { - * const char *cname; (Name der Zauberwirkung, Identifizierung des curse) - * int typ; - * spread_t spread; - * unsigned int mergeflags; - * int (*curseinfo)(const struct locale*, const void*, int, curse*, int); - * void (*change_vigour)(curse*, double); - * int (*read)(struct storage * store, curse * c); - * int (*write)(struct storage * store, const curse * c); - * } curse_type; - */ - -int resolve_curse(variant id, void *address) -{ - int result = 0; - curse *c = NULL; - if (id.i != 0) { - c = cfindhash(id.i); - if (c == NULL) { - result = -1; - } - } - *(curse **)address = c; - return result; -} static const char *oldnames[MAXCURSE] = { /* OBS: when removing curses, remember to update read_ccompat() */ @@ -750,7 +725,6 @@ static const char *oldnames[MAXCURSE] = { "oldrace", "fumble", "riotzone", - "nocostbuilding", "godcursezone", "speed", "orcish", @@ -821,9 +795,9 @@ double destr_curse(curse * c, int cast_level, double force) return force; } -void free_curses(void) { +void curses_done(void) { int i; for (i = 0; i != MAXCTHASH; ++i) { ql_free(cursetypes[i]); } -} \ No newline at end of file +} diff --git a/src/kernel/curse.h b/src/kernel/curse.h index 263b66125..c78e43405 100644 --- a/src/kernel/curse.h +++ b/src/kernel/curse.h @@ -28,6 +28,8 @@ extern "C" { struct curse; struct curse_type; + struct gamedata; + struct storage; /* Sprueche in der struct region und auf Einheiten, Schiffen oder Burgen * (struct attribute) @@ -93,7 +95,7 @@ extern "C" { * * */ -#include + extern struct attrib_type at_curse; /* ------------------------------------------------------------- */ /* Zauberwirkungen */ @@ -128,7 +130,6 @@ extern "C" { C_OLDRACE, C_FUMBLE, C_RIOT, /*region in Aufruhr */ - C_NOCOST, C_CURSED_BY_THE_GODS, C_SPEED, /* Beschleunigt */ C_ORC, @@ -194,8 +195,8 @@ extern "C" { struct message *(*curseinfo) (const void *, objtype_t, const struct curse *, int); void(*change_vigour) (struct curse *, double); - int(*read) (struct storage * store, struct curse *, void *target); - int(*write) (struct storage * store, const struct curse *, + int(*read) (struct gamedata *data, struct curse *, void *target); + int(*write) (struct storage *store, const struct curse *, const void *target); int(*cansee) (const struct faction *, const void *, objtype_t, const struct curse *, int); @@ -214,12 +215,11 @@ extern "C" { int duration; /* Dauer der Verzauberung. Wird jede Runde vermindert */ } curse; - void free_curses(void); /* de-register all curse-types */ + void curses_done(void); /* de-register all curse-types */ - extern struct attrib_type at_curse; void curse_write(const struct attrib *a, const void *owner, - struct storage *store); - int curse_read(struct attrib *a, void *owner, struct storage *store); + struct storage *store); + int curse_read(struct attrib *a, void *owner, struct gamedata *store); /* ------------------------------------------------------------- */ /* Kommentare: @@ -285,8 +285,6 @@ extern "C" { void ct_register(const curse_type *); void ct_checknames(void); - curse *cfindhash(int i); - curse *findcurse(int curseid); void curse_init(struct attrib *a); @@ -295,7 +293,6 @@ extern "C" { double destr_curse(struct curse *c, int cast_level, double force); - int resolve_curse(variant data, void *address); bool is_cursed_with(const struct attrib *ap, const struct curse *c); /* gibt true, wenn der Curse nicht NULL oder inaktiv ist */ diff --git a/src/kernel/curse.test.c b/src/kernel/curse.test.c index 002ef18ec..2be81b19c 100644 --- a/src/kernel/curse.test.c +++ b/src/kernel/curse.test.c @@ -2,9 +2,11 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -52,6 +54,11 @@ static void setup_curse(curse_fixture *fix, const char *name) { fix->c = create_curse(fix->u, &fix->r->attribs, ct_find(name), 1.0, 1, 1.0, 0); } +static void cleanup_curse(curse_fixture *fix) { + // destroy_curse(fix->c); + test_cleanup(); +} + static void test_magicstreet(CuTest *tc) { curse_fixture fix; message *msg; @@ -60,7 +67,7 @@ static void test_magicstreet(CuTest *tc) { msg = fix.c->type->curseinfo(fix.r, TYP_REGION, fix.c, 0); CuAssertStrEquals(tc, "curseinfo::magicstreet", test_get_messagetype(msg)); msg_release(msg); - test_cleanup(); + cleanup_curse(&fix); } static void test_magicstreet_warning(CuTest *tc) { @@ -71,7 +78,7 @@ static void test_magicstreet_warning(CuTest *tc) { msg = fix.c->type->curseinfo(fix.r, TYP_REGION, fix.c, 0); CuAssertStrEquals(tc, "curseinfo::magicstreetwarn", test_get_messagetype(msg)); msg_release(msg); - test_cleanup(); + cleanup_curse(&fix); } static void test_good_dreams(CuTest *tc) { @@ -82,7 +89,7 @@ static void test_good_dreams(CuTest *tc) { msg = fix.c->type->curseinfo(fix.r, TYP_REGION, fix.c, 0); CuAssertStrEquals(tc, "curseinfo::gooddream", test_get_messagetype(msg)); msg_release(msg); - test_cleanup(); + cleanup_curse(&fix); } static void test_bad_dreams(CuTest *tc) { @@ -93,7 +100,7 @@ static void test_bad_dreams(CuTest *tc) { msg = fix.c->type->curseinfo(fix.r, TYP_REGION, fix.c, 0); CuAssertStrEquals(tc, "curseinfo::baddream", test_get_messagetype(msg)); msg_release(msg); - test_cleanup(); + cleanup_curse(&fix); } static void test_memstream(CuTest *tc) { @@ -122,30 +129,33 @@ static void test_memstream(CuTest *tc) { static void test_write_flag(CuTest *tc) { curse_fixture fix; + gamedata data; storage store; - char buf[1024]; - stream out = { 0 }; - size_t len; + region * r; + curse * c; + int uid; - mstream_init(&out); - binstore_init(&store, &out); - store.handle.data = &out; + mstream_init(&data.strm); + gamedata_init(&data, &store, RELEASE_VERSION); setup_curse(&fix, "gbdream"); - fix.c->flags = 42 | CURSE_ISNEW; - curse_write(fix.r->attribs, fix.r, &store); - out.api->rewind(out.handle); - len = out.api->read(out.handle, buf, sizeof(buf)); - buf[len] = '\0'; - out.api->rewind(out.handle); - curse_read(fix.r->attribs, fix.r, &store); - CuAssertIntEquals(tc, 42 | CURSE_ISNEW, ((curse *) fix.r->attribs->data.v)->flags); - global.data_version = RELEASE_VERSION; - CuAssertIntEquals(tc, RELEASE_VERSION, global.data_version); + c = fix.c; + r = fix.r; + uid = r->uid; + c->flags = CURSE_ISNEW; + write_game(&data); + free_gamedata(); + data.strm.api->rewind(data.strm.handle); + read_game(&data); + r = findregionbyid(uid); + CuAssertPtrNotNull(tc, r); + CuAssertPtrNotNull(tc, r->attribs); + c = (curse *)r->attribs->data.v; + CuAssertIntEquals(tc, CURSE_ISNEW, c->flags); - mstream_done(&out); - binstore_done(&store); - test_cleanup(); + mstream_done(&data.strm); + gamedata_done(&data); + cleanup_curse(&fix); } CuSuite *get_curse_suite(void) diff --git a/src/kernel/equipment.test.c b/src/kernel/equipment.test.c index 851e591d2..f0543ad8c 100644 --- a/src/kernel/equipment.test.c +++ b/src/kernel/equipment.test.c @@ -36,7 +36,7 @@ void test_equipment(CuTest * tc) equipment_setskill(eq, SK_MAGIC, "5"); equipment_addspell(eq, sp, 1); - u = test_create_unit(0, 0); + u = test_create_unit(test_create_faction(0), 0); equip_unit_mask(u, eq, EQUIP_ALL); CuAssertIntEquals(tc, 1, i_get(u->items, it_horses)); CuAssertIntEquals(tc, 5, get_level(u, SK_MAGIC)); diff --git a/src/kernel/faction.c b/src/kernel/faction.c index c7f9498b8..c1429f4f3 100755 --- a/src/kernel/faction.c +++ b/src/kernel/faction.c @@ -40,6 +40,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include #include #include @@ -322,10 +323,10 @@ bool checkpasswd(const faction * f, const char *passwd) return true; } -variant read_faction_reference(struct storage * store) +variant read_faction_reference(gamedata * data) { variant id; - READ_INT(store, &id.i); + READ_INT(data->store, &id.i); return id; } @@ -339,7 +340,7 @@ static faction *dead_factions; void free_flist(faction **fp) { faction * flist = *fp; - for (flist = factions; flist;) { + while (flist) { faction *f = flist; flist = f->next; free_faction(f); @@ -796,6 +797,7 @@ attrib_type at_maxmagicians = { NULL, a_writeint, a_readint, + NULL, ATF_UNIQUE }; @@ -851,7 +853,7 @@ int writepasswd(void) FILE *F; char zText[128]; - sprintf(zText, "%s/passwd", basepath()); + join_path(basepath(), "passwd", zText, sizeof(zText)); F = fopen(zText, "w"); if (!F) { perror(zText); diff --git a/src/kernel/faction.h b/src/kernel/faction.h index 6369444f5..5f7e1f91f 100644 --- a/src/kernel/faction.h +++ b/src/kernel/faction.h @@ -30,7 +30,8 @@ extern "C" { struct item; struct seen_region; struct attrib_type; - + struct gamedata; + extern struct attrib_type at_maxmagicians; /* SMART_INTERVALS: define to speed up finding the interval of regions that a faction is in. defining this speeds up the turn by 30-40% */ @@ -133,7 +134,7 @@ extern "C" { void write_faction_reference(const struct faction *f, struct storage *store); - variant read_faction_reference(struct storage *store); + variant read_faction_reference(struct gamedata *data); int resolve_faction(variant data, void *addr); void renumber_faction(faction * f, int no); diff --git a/src/kernel/faction.test.c b/src/kernel/faction.test.c index 10bc0683b..ecd0df2ab 100644 --- a/src/kernel/faction.test.c +++ b/src/kernel/faction.test.c @@ -103,7 +103,7 @@ static void test_addfaction(CuTest *tc) { test_cleanup(); rc = rc_get_or_create("human"); - lang = get_or_create_locale("en"); + lang = test_create_locale(); f = addfaction("test@eressea.de", "hurrdurr", rc, lang, 1234); CuAssertPtrNotNull(tc, f); CuAssertPtrNotNull(tc, f->name); diff --git a/src/kernel/group.c b/src/kernel/group.c index fdef2b8b8..19f816b13 100755 --- a/src/kernel/group.c +++ b/src/kernel/group.c @@ -33,6 +33,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* util includes */ #include #include +#include #include #include @@ -95,8 +96,9 @@ static group *find_group(int gid) return g; } -static int read_group(attrib * a, void *owner, struct storage *store) +static int read_group(attrib * a, void *owner, gamedata *data) { + struct storage *store = data->store; group *g; int gid; @@ -119,7 +121,7 @@ write_group(const attrib * a, const void *owner, struct storage *store) attrib_type at_group = { /* attribute for units assigned to a group */ "grp", DEFAULT_INIT, - DEFAULT_FINALIZE, DEFAULT_AGE, write_group, read_group, ATF_UNIQUE }; + DEFAULT_FINALIZE, DEFAULT_AGE, write_group, read_group, NULL, ATF_UNIQUE }; void free_group(group * g) { @@ -130,6 +132,9 @@ void free_group(group * g) assert(*g_ptr == g); *g_ptr = g->nexthash; + if (g->attribs) { + a_removeall(&g->attribs, NULL); + } while (g->allies) { ally *a = g->allies; g->allies = a->next; @@ -215,8 +220,9 @@ void write_groups(struct storage *store, const faction * f) WRITE_INT(store, 0); } -void read_groups(struct storage *store, faction * f) +void read_groups(gamedata *data, faction * f) { + struct storage *store = data->store; for (;;) { ally **pa; group *g; @@ -233,7 +239,7 @@ void read_groups(struct storage *store, faction * f) ally *a; variant fid; - fid = read_faction_reference(store); + fid = read_faction_reference(data); if (fid.i <= 0) break; a = ally_add(pa, findfaction(fid.i)); @@ -241,6 +247,6 @@ void read_groups(struct storage *store, faction * f) if (!a->faction) ur_add(fid, &a->faction, resolve_faction); } - a_read(store, &g->attribs, g); + read_attribs(data, &g->attribs, g); } } diff --git a/src/kernel/group.h b/src/kernel/group.h index 145881c00..b257f515a 100755 --- a/src/kernel/group.h +++ b/src/kernel/group.h @@ -43,7 +43,7 @@ extern "C" { struct group *new_group(struct faction * f, const char *name, int gid); extern void write_groups(struct storage *data, const struct faction *f); - extern void read_groups(struct storage *data, struct faction *f); + extern void read_groups(struct gamedata *data, struct faction *f); #ifdef __cplusplus } diff --git a/src/kernel/group.test.c b/src/kernel/group.test.c index 7daca2829..7fa7fa234 100644 --- a/src/kernel/group.test.c +++ b/src/kernel/group.test.c @@ -1,55 +1,103 @@ #include +#include "config.h" #include "types.h" #include "ally.h" #include "group.h" #include "faction.h" #include "unit.h" #include "region.h" +#include "save.h" +#include "version.h" + +#include #include #include + #include #include #include +#include #include #include #include #include +static void test_group_readwrite_dead_faction(CuTest *tc) { + gamedata data; + storage store; + faction *f, *f2; + unit * u; + group *g; + ally *al; + int fno; + + test_cleanup(); + f = test_create_faction(0); + fno = f->no; + CuAssertPtrEquals(tc, f, factions); + CuAssertPtrEquals(tc, 0, f->next); + f2 = test_create_faction(0); + CuAssertPtrEquals(tc, f2, factions->next); + u = test_create_unit(f2, test_create_region(0, 0, 0)); + CuAssertPtrNotNull(tc, u); + g = join_group(u, "group"); + CuAssertPtrNotNull(tc, g); + al = ally_add(&g->allies, f); + CuAssertPtrNotNull(tc, al); + + CuAssertPtrEquals(tc, f, factions); + destroyfaction(&factions); + CuAssertTrue(tc, !f->_alive); + CuAssertPtrEquals(tc, f2, factions); + mstream_init(&data.strm); + gamedata_init(&data, &store, RELEASE_VERSION); + write_game(&data); + free_gamedata(); + f = f2 = NULL; + data.strm.api->rewind(data.strm.handle); + read_game(&data); + mstream_done(&data.strm); + gamedata_done(&data); + CuAssertPtrEquals(tc, 0, findfaction(fno)); + f2 = factions; + CuAssertPtrNotNull(tc, f2); + u = f2->units; + CuAssertPtrNotNull(tc, u); + g = get_group(u); + CuAssertPtrNotNull(tc, g); + CuAssertPtrEquals(tc, 0, g->allies); + test_cleanup(); +} + static void test_group_readwrite(CuTest * tc) { faction * f; group *g; ally *al; - storage store; - FILE *F; - stream strm; int i; + gamedata data; + storage store; - F = fopen("test.dat", "wb"); - fstream_init(&strm, F); - binstore_init(&store, &strm); test_cleanup(); - test_create_world(); + mstream_init(&data.strm); + gamedata_init(&data, &store, RELEASE_VERSION); f = test_create_faction(0); g = new_group(f, "NW", 42); g = new_group(f, "Egoisten", 43); - a_add(&g->attribs, make_key(44)); + key_set(&g->attribs, 44); al = ally_add(&g->allies, f); al->status = HELP_GIVE; write_groups(&store, f); WRITE_INT(&store, 47); - binstore_done(&store); - fstream_done(&strm); - F = fopen("test.dat", "rb"); - fstream_init(&strm, F); - binstore_init(&store, &strm); + free_group(g); f->groups = 0; - read_groups(&store, f); + data.strm.api->rewind(data.strm.handle); + read_groups(&data, f); READ_INT(&store, &i); - binstore_done(&store); - fstream_done(&strm); + mstream_done(&data.strm); + gamedata_done(&data); CuAssertIntEquals(tc, 47, i); CuAssertPtrNotNull(tc, f->groups); @@ -60,12 +108,11 @@ static void test_group_readwrite(CuTest * tc) CuAssertStrEquals(tc, "Egoisten", f->groups->next->name); CuAssertPtrEquals(tc, 0, f->groups->allies); g = f->groups->next; - CuAssertPtrNotNull(tc, find_key(g->attribs, 44)); + CuAssertTrue(tc, key_get(g->attribs, 44)); CuAssertPtrNotNull(tc, g->allies); CuAssertPtrEquals(tc, 0, g->allies->next); CuAssertPtrEquals(tc, f, g->allies->faction); CuAssertIntEquals(tc, HELP_GIVE, g->allies->status); - remove("test.dat"); test_cleanup(); } @@ -77,8 +124,7 @@ static void test_group(CuTest * tc) group *g; test_cleanup(); - test_create_world(); - r = findregion(0, 0); + r = test_create_region(0, 0, 0); f = test_create_faction(0); assert(r && f); u = test_create_unit(f, r); @@ -101,6 +147,7 @@ CuSuite *get_group_suite(void) CuSuite *suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_group); SUITE_ADD_TEST(suite, test_group_readwrite); + SUITE_ADD_TEST(suite, test_group_readwrite_dead_faction); return suite; } diff --git a/src/kernel/item.c b/src/kernel/item.c index 190e461f2..182e71fbe 100644 --- a/src/kernel/item.c +++ b/src/kernel/item.c @@ -926,14 +926,14 @@ struct order *ord) "unit item region command", user, itype->rtype, user->region, ord)); return -1; } - if (!is_mage(user) || find_key(f->attribs, atoi36("mbst")) != NULL) { + 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); - a_add(&f->attribs, make_key(atoi36("mbst"))); + key_set(&f->attribs, atoi36("mbst")); set_level(user, SK_MAGIC, 3); ADDMSG(&user->faction->msgs, msg_message("use_item", @@ -1188,6 +1188,7 @@ static item *default_spoil(const struct race *rc, int size) } static void free_itype(item_type *itype) { + assert(itype); free(itype->construction); free(itype->_appearance[0]); free(itype->_appearance[1]); @@ -1195,22 +1196,31 @@ static void free_itype(item_type *itype) { } static void free_wtype(weapon_type *wtype) { + assert(wtype); free(wtype->damage[0]); free(wtype->damage[1]); free(wtype); } -int free_rtype_cb(const void * match, const void * key, size_t keylen, void *cbdata) { - resource_type *rtype; - cb_get_kv(match, &rtype, sizeof(rtype)); - free(rtype->_name); - if (rtype->itype) { - free_itype(rtype->itype); - } +void free_rtype(resource_type *rtype) { + assert(rtype); if (rtype->wtype) { free_wtype(rtype->wtype); } + if (rtype->atype) { + free(rtype->atype); + } + if (rtype->itype) { + free_itype(rtype->itype); + } + free(rtype->_name); free(rtype); +} + +int free_rtype_cb(const void * match, const void * key, size_t keylen, void *cbdata) { + resource_type *rtype; + cb_get_kv(match, &rtype, sizeof(rtype)); + free_rtype(rtype); return 0; } diff --git a/src/kernel/item.test.c b/src/kernel/item.test.c index 2d76422ff..1c133b74f 100644 --- a/src/kernel/item.test.c +++ b/src/kernel/item.test.c @@ -64,7 +64,7 @@ void test_change_item(CuTest * tc) test_create_itemtype("iron"); init_resources(); - u = test_create_unit(0, 0); + u = test_create_unit(test_create_faction(0), 0); test_uchange(tc, u, get_resourcetype(R_IRON)); } diff --git a/src/kernel/jsonconf.c b/src/kernel/jsonconf.c index 099b7a228..721e6e60f 100644 --- a/src/kernel/jsonconf.c +++ b/src/kernel/jsonconf.c @@ -511,7 +511,7 @@ static void json_prefixes(cJSON *json) { } /** disable a feature. - * features are identified by eone of: + * features are identified by one of: * 1. the keyword for their orders, * 2. the name of the skill they use, * 3. a "module.enabled" flag in the settings @@ -525,13 +525,11 @@ static void disable_feature(const char *str) { enable_skill(sk, false); return; } - for (k = 0; k != MAXKEYWORDS; ++k) { - // FIXME: this loop is slow as balls. - if (strcmp(keywords[k], str) == 0) { - log_debug("disable keyword %s\n", str); - enable_keyword(k, false); - return; - } + k = findkeyword(str); + if (k!=NOKEYWORD) { + log_debug("disable keyword %s\n", str); + enable_keyword(k, false); + return; } _snprintf(name, sizeof(name), "%s.enabled", str); log_info("disable feature %s\n", name); @@ -828,11 +826,11 @@ static void json_include(cJSON *json) { FILE *F; if (json_relpath) { char name[MAX_PATH]; - _snprintf(name, sizeof(name), "%s/%s", json_relpath, child->valuestring); - F = fopen(name, "rt"); + join_path(json_relpath, child->valuestring, name, sizeof(name)); + F = fopen(name, "r"); } else { - F = fopen(child->valuestring, "rt"); + F = fopen(child->valuestring, "r"); } if (F) { long pos; diff --git a/src/kernel/jsonconf.test.c b/src/kernel/jsonconf.test.c index 5e7d3047e..858c811b9 100644 --- a/src/kernel/jsonconf.test.c +++ b/src/kernel/jsonconf.test.c @@ -438,7 +438,7 @@ static void test_configs(CuTest * tc) test_cleanup(); - F = fopen("test.json", "wt"); + F = fopen("test.json", "w"); fwrite(building_data, 1, strlen(building_data), F); fclose(F); CuAssertPtrNotNull(tc, json); @@ -458,7 +458,7 @@ static void test_terrains(CuTest * tc) "\"size\": 4000, " "\"road\": 50, " "\"seed\": 3, " - "\"flags\" : [ \"forbidden\", \"arctic\", \"cavalry\", \"sea\", \"forest\", \"land\", \"sail\", \"fly\", \"swim\", \"walk\" ] } }}"; + "\"flags\" : [ \"forbidden\", \"arctic\", \"cavalry\", \"sea\", \"forest\", \"land\", \"fly\", \"swim\", \"walk\" ] } }}"; const terrain_type *ter; cJSON *json = cJSON_Parse(data); @@ -470,7 +470,7 @@ static void test_terrains(CuTest * tc) json_config(json); ter = get_terrain("plain"); CuAssertPtrNotNull(tc, ter); - CuAssertIntEquals(tc, ARCTIC_REGION | LAND_REGION | SEA_REGION | FOREST_REGION | CAVALRY_REGION | FORBIDDEN_REGION | FLY_INTO | WALK_INTO | SWIM_INTO | SAIL_INTO, ter->flags); + CuAssertIntEquals(tc, ARCTIC_REGION | LAND_REGION | SEA_REGION | FOREST_REGION | CAVALRY_REGION | FORBIDDEN_REGION | FLY_INTO | WALK_INTO | SWIM_INTO , ter->flags); CuAssertIntEquals(tc, 4000, ter->size); CuAssertIntEquals(tc, 50, ter->max_road); CuAssertIntEquals(tc, 3, ter->distribution); diff --git a/src/kernel/pathfinder.c b/src/kernel/pathfinder.c index 683da5699..8ae9bf128 100644 --- a/src/kernel/pathfinder.c +++ b/src/kernel/pathfinder.c @@ -28,6 +28,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include +#define MAXDEPTH 1024 + bool allowed_swim(const region * src, const region * r) { if (fval(r->terrain, SWIM_INTO)) diff --git a/src/kernel/pathfinder.h b/src/kernel/pathfinder.h index b7ab0f779..81692e835 100644 --- a/src/kernel/pathfinder.h +++ b/src/kernel/pathfinder.h @@ -22,11 +22,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. extern "C" { #endif -#define MAXDEPTH 1024 - - extern int search[MAXDEPTH][2]; - extern int search_len; - extern struct region **path_find(struct region *start, const struct region *target, int maxlen, bool(*allowed) (const struct region *, const struct region *)); diff --git a/src/kernel/plane.c b/src/kernel/plane.c index 2542a053d..7e9650d13 100644 --- a/src/kernel/plane.c +++ b/src/kernel/plane.c @@ -290,3 +290,33 @@ int read_plane_reference(plane ** pp, struct storage *store) ur_add(id, pp, resolve_plane); return AT_READ_OK; } + +void free_plane(plane *pl) { + free(pl->name); + free(pl); +} + +void remove_plane(plane *pl) { + region **rp = ®ions; + plane **pp = &planes; + assert(pl); + while (*rp) { + region *r = *rp; + if (r->_plane == pl) { + remove_region(rp, r); + } + else { + rp = &r->next; + } + } + while (*pp) { + if (pl==*pp) { + *pp = pl->next; + free_plane(pl); + break; + } + else { + pp = &(*pp)->next; + } + } +} diff --git a/src/kernel/plane.h b/src/kernel/plane.h index 926ffb4e2..85c38cf9f 100644 --- a/src/kernel/plane.h +++ b/src/kernel/plane.h @@ -60,7 +60,6 @@ extern "C" { struct plane *getplane(const struct region *r); struct plane *findplane(int x, int y); - void init_planes(void); int getplaneid(const struct region *r); struct plane *getplanebyid(int id); int plane_center_x(const struct plane *pl); @@ -69,13 +68,16 @@ extern "C" { int miny, int maxy, int flags); struct plane *getplanebyname(const char *); struct plane *get_homeplane(void); - extern int rel_to_abs(const struct plane *pl, const struct faction *f, + int rel_to_abs(const struct plane *pl, const struct faction *f, int rel, unsigned char index); - extern void write_plane_reference(const plane * p, struct storage *store); - extern int read_plane_reference(plane ** pp, struct storage *store); - extern int plane_width(const plane * pl); - extern int plane_height(const plane * pl); + void write_plane_reference(const plane * p, struct storage *store); + int read_plane_reference(plane ** pp, struct storage *store); + int plane_width(const plane * pl); + int plane_height(const plane * pl); void adjust_coordinates(const struct faction *f, int *x, int *y, const struct plane *pl); + + void free_plane(struct plane *pl); + void remove_plane(struct plane *pl); #ifdef __cplusplus } #endif diff --git a/src/kernel/plane.test.c b/src/kernel/plane.test.c new file mode 100644 index 000000000..a479a34be --- /dev/null +++ b/src/kernel/plane.test.c @@ -0,0 +1,67 @@ +#include +#include +#include "plane.h" +#include "faction.h" +#include +#include + +static void test_plane(CuTest *tc) { + struct region *r; + plane *pl; + + test_cleanup(); + r = test_create_region(0, 0, 0); + CuAssertPtrEquals(tc, 0, findplane(0, 0)); + CuAssertPtrEquals(tc, 0, getplane(r)); + CuAssertIntEquals(tc, 0, getplaneid(r)); + CuAssertPtrEquals(tc, 0, getplanebyid(0)); + CuAssertIntEquals(tc, 0, plane_center_x(0)); + CuAssertIntEquals(tc, 0, plane_center_y(0)); + CuAssertIntEquals(tc, 0, plane_width(0)); + CuAssertIntEquals(tc, 0, plane_height(0)); + CuAssertPtrEquals(tc, 0, get_homeplane()); + + pl = create_new_plane(1, "Hell", 4, 8, 40, 80, 15); + r = test_create_region(4, 40, 0); + CuAssertIntEquals(tc, 15, pl->flags); + CuAssertIntEquals(tc, 4, pl->minx); + CuAssertIntEquals(tc, 8, pl->maxx); + CuAssertIntEquals(tc, 40, pl->miny); + CuAssertIntEquals(tc, 80, pl->maxy); + CuAssertPtrEquals(tc, 0, pl->attribs); + CuAssertStrEquals(tc, "Hell", pl->name); + CuAssertPtrEquals(tc, pl, findplane(4, 40)); + CuAssertPtrEquals(tc, pl, getplane(r)); + CuAssertPtrEquals(tc, pl, getplanebyid(1)); + CuAssertIntEquals(tc, 1, getplaneid(r)); + CuAssertIntEquals(tc, 6, plane_center_x(pl)); + CuAssertIntEquals(tc, 60, plane_center_y(pl)); + CuAssertIntEquals(tc, 5, plane_width(pl)); + CuAssertIntEquals(tc, 41, plane_height(pl)); +} + +static void test_origin(CuTest *tc) { + struct faction *f; + int x, y; + + test_cleanup(); + f = test_create_faction(0); + x = 0; + y = 0; + adjust_coordinates(f, &x, &y, 0); + CuAssertIntEquals(tc, 0, x); + CuAssertIntEquals(tc, 0, y); + faction_setorigin(f, 0, 10, 20); + adjust_coordinates(f, &x, &y, 0); + CuAssertIntEquals(tc, -10, x); + CuAssertIntEquals(tc, -20, y); + test_cleanup(); +} + +CuSuite *get_plane_suite(void) +{ + CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_plane); + SUITE_ADD_TEST(suite, test_origin); + return suite; +} diff --git a/src/kernel/race.c b/src/kernel/race.c index f372bb8ae..be9f2ab68 100644 --- a/src/kernel/race.c +++ b/src/kernel/race.c @@ -141,6 +141,7 @@ void racelist_insert(struct race_list **rl, const struct race *r) void free_races(void) { while (races) { race * rc = races->next; + free_params(&races->parameters); free(races->_name); free(races->def_damage); free(races); @@ -242,10 +243,10 @@ const char *rc_name_s(const race * rc, name_t n) const char *raceprefix(const unit * u) { - const attrib *asource = u->faction->attribs; + attrib *asource = u->faction->attribs; if (fval(u, UFL_GROUP)) { - const attrib *agroup = a_findc(u->attribs, &at_group); + attrib *agroup = a_find(u->attribs, &at_group); if (agroup != NULL) asource = ((const group *)(agroup->data.v))->attribs; } diff --git a/src/kernel/region.c b/src/kernel/region.c index 80a87552e..54e78ec06 100644 --- a/src/kernel/region.c +++ b/src/kernel/region.c @@ -42,6 +42,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* util includes */ #include #include +#include #include #include #include @@ -176,12 +177,12 @@ void a_initmoveblock(attrib * a) a->data.v = calloc(1, sizeof(moveblock)); } -int a_readmoveblock(attrib * a, void *owner, struct storage *store) +int a_readmoveblock(attrib * a, void *owner, gamedata *data) { moveblock *m = (moveblock *)(a->data.v); int i; - READ_INT(store, &i); + READ_INT(data->store, &i); m->dir = (direction_t)i; return AT_READ_OK; } @@ -483,6 +484,7 @@ attrib_type at_horseluck = { DEFAULT_AGE, NO_WRITE, NO_READ, + NULL, ATF_UNIQUE }; @@ -496,6 +498,7 @@ attrib_type at_peasantluck = { DEFAULT_AGE, NO_WRITE, NO_READ, + NULL, ATF_UNIQUE }; @@ -509,6 +512,7 @@ attrib_type at_deathcount = { DEFAULT_AGE, a_writeint, a_readint, + NULL, ATF_UNIQUE }; @@ -522,6 +526,7 @@ attrib_type at_woodcount = { DEFAULT_AGE, NO_WRITE, a_readint, + NULL, ATF_UNIQUE }; @@ -633,27 +638,46 @@ int rhorses(const region * r) void rsetmoney(region * r, int value) { - assert(r->land || value==0); + assert(r && (r->land || value==0)); assert(value >= 0); if (r->land) { r->land->money = value; } } -int rherbs(const struct region *r) +int rherbs(const region *r) { return r->land?r->land->herbs:0; } -void rsetherbs(const struct region *r, int value) +void rsetherbs(region *r, int value) { assert(r->land || value==0); - assert(value >= 0); + assert(value >= 0 && value<=SHRT_MAX); if (r->land) { - r->land->herbs = (short)(value); + r->land->herbs = (short)value; } } +void rsetherbtype(region *r, const struct item_type *itype) { + assert(r->land && r->terrain); + if (itype == NULL) { + r->land->herbtype = NULL; + } + else { + if (r->terrain->herbs) { + int i; + for (i = 0; r->terrain->herbs[i]; ++i) { + if (r->terrain->herbs[i] == itype) { + r->land->herbtype = itype; + return; + } + } + } + log_debug("attempt to set herbtype=%s for terrain=%s in %s", itype->rtype->_name, r->terrain->_name, regionname(r, 0)); + r->land->herbtype = itype; + } +} void r_setdemand(region * r, const luxury_type * ltype, int value) { @@ -1221,10 +1245,11 @@ int resolve_region_id(variant id, void *address) return 0; } -variant read_region_reference(struct storage * store) +variant read_region_reference(gamedata *data) { + struct storage * store = data->store; variant result; - if (global.data_version < UIDHASH_VERSION) { + if (data->version < UIDHASH_VERSION) { int n; READ_INT(store, &n); result.sa[0] = (short)n; diff --git a/src/kernel/region.h b/src/kernel/region.h index 72496193b..bbc5282b0 100644 --- a/src/kernel/region.h +++ b/src/kernel/region.h @@ -18,9 +18,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #ifndef H_KRNL_REGION #define H_KRNL_REGION -#ifdef __cplusplus -extern "C" { -#endif #include #include "types.h" @@ -67,6 +64,7 @@ extern "C" { struct rawmaterial; struct item; struct faction; + struct gamedata; #define MORALE_TAX_FACTOR 0.005 /* 0.5% tax per point of morale */ #define MORALE_MAX 10 /* Maximum morale allowed */ @@ -76,6 +74,11 @@ extern "C" { #define MORALE_AVERAGE 6 /* default average time for morale to change */ #define MORALE_TRANSFER 2 /* points of morale lost when GIVE COMMAND */ +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct region_owner { struct faction *owner; struct faction *last_owner; @@ -165,7 +168,6 @@ extern "C" { extern struct attrib_type at_woodcount; extern struct attrib_type at_deathcount; - void initrhash(void); void rhash(struct region *r); void runhash(struct region *r); @@ -173,10 +175,7 @@ extern "C" { void add_regionlist(region_list ** rl, struct region *r); int deathcount(const struct region *r); - int chaoscount(const struct region *r); - void deathcounts(struct region *r, int delta); - void chaoscounts(struct region *r, int delta); void setluxuries(struct region *r, const struct luxury_type *sale); int get_maxluxuries(void); @@ -203,12 +202,12 @@ extern "C" { void rsethorses(const struct region *r, int value); int rherbs(const struct region *r); - void rsetherbs(const struct region *r, int value); + void rsetherbs(struct region *r, int value); + void rsetherbtype(struct region *r, const struct item_type *itype); #define rbuildings(r) ((r)->buildings) #define rherbtype(r) ((r)->land?(r)->land->herbtype:0) -#define rsetherbtype(r, value) if ((r)->land) (r)->land->herbtype=(value) bool r_isforest(const struct region *r); @@ -255,7 +254,7 @@ extern "C" { void region_set_morale(region * r, int morale, int turn); void write_region_reference(const struct region *r, struct storage *store); - variant read_region_reference(struct storage *store); + variant read_region_reference(struct gamedata *data); int resolve_region_coor(variant id, void *address); int resolve_region_id(variant id, void *address); #define RESOLVE_REGION(version) ((version #include #include +#include #include +#include #include #include #include @@ -172,7 +174,7 @@ static unit *unitorders(FILE * F, int enc, struct faction *f) if (s[0] != '@') { char token[128]; const char *stok = s; - stok = parse_token(&stok, token, sizeof(token)); + stok = parse_token(&stok, token, 64); // FIXME: use sizeof, but parse_token overwrites the buffer if (stok) { bool quit = false; @@ -238,8 +240,8 @@ static faction *factionorders(void) /* Die Partei hat sich zumindest gemeldet, so dass sie noch * nicht als untätig gilt */ - /* TODO: +1 ist ein Workaround, weil cturn erst in process_orders - * incrementiert wird. */ + /* TODO: +1 ist ein Workaround, weil cturn erst in process_orders + * incrementiert wird. */ f->lastorders = global.data_turn + 1; } @@ -417,11 +419,12 @@ void read_items(struct storage *store, item ** ilist) } } -static void read_alliances(struct storage *store) +static void read_alliances(struct gamedata *data) { + storage *store = data->store; char pbuf[8]; int id, terminator = 0; - if (global.data_version < ALLIANCELEADER_VERSION) { + if (data->version < ALLIANCELEADER_VERSION) { terminator = atoi36("end"); READ_STR(store, pbuf, sizeof(pbuf)); id = atoi36(pbuf); @@ -433,12 +436,12 @@ static void read_alliances(struct storage *store) char aname[128]; alliance *al; READ_STR(store, aname, sizeof(aname)); - al = makealliance(id, aname); - if (global.data_version >= OWNER_2_VERSION) { + al = new_alliance(id, aname); + if (data->version >= OWNER_2_VERSION) { READ_INT(store, &al->flags); } - if (global.data_version >= ALLIANCELEADER_VERSION) { - read_reference(&al->_leader, store, read_faction_reference, + if (data->version >= ALLIANCELEADER_VERSION) { + read_reference(&al->_leader, data, read_faction_reference, resolve_faction); READ_INT(store, &id); } @@ -449,12 +452,93 @@ static void read_alliances(struct storage *store) } } +void read_planes(gamedata *data) { + struct storage *store = data->store; + int nread; + char name[32]; + + /* Planes */ + planes = NULL; + READ_INT(store, &nread); + while (--nread >= 0) { + int id; + variant fno; + plane *pl; + + READ_INT(store, &id); + pl = getplanebyid(id); + + if (pl == NULL) { + pl = calloc(1, sizeof(plane)); + } + else { + log_warning("the plane with id=%d already exists.", id); + } + pl->id = id; + READ_STR(store, name, sizeof(name)); + pl->name = _strdup(name); + READ_INT(store, &pl->minx); + READ_INT(store, &pl->maxx); + READ_INT(store, &pl->miny); + READ_INT(store, &pl->maxy); + READ_INT(store, &pl->flags); + + /* read watchers */ + if (data->version < FIX_WATCHERS_VERSION) { + char rname[64]; + /* before this version, watcher storage was pretty broken. we are incompatible and don't read them */ + for (;;) { + READ_TOK(store, rname, sizeof(rname)); + if (strcmp(rname, "end") == 0) { + break; /* this is most likely the end of the list */ + } + else { + log_error( + ("This datafile contains watchers, but we are unable to read them.")); + } + } + } + else { + /* WATCHERS - eliminated in February 2016, ca. turn 966 */ + if (data->version < NOWATCH_VERSION) { + fno = read_faction_reference(data); + while (fno.i) { + fno = read_faction_reference(data); + } + } + } + read_attribs(data, &pl->attribs, pl); + if (pl->id != 1094969858) { // Regatta + addlist(&planes, pl); + } + } +} + +void write_planes(storage *store) { + plane *pl; + /* Write planes */ + WRITE_INT(store, listlen(planes)); + for (pl = planes; pl; pl = pl->next) { + WRITE_INT(store, pl->id); + WRITE_STR(store, pl->name); + WRITE_INT(store, pl->minx); + WRITE_INT(store, pl->maxx); + WRITE_INT(store, pl->miny); + WRITE_INT(store, pl->maxy); + WRITE_INT(store, pl->flags); +#if RELEASE_VERSION < NOWATCH_VERSION + write_faction_reference(NULL, store); /* mark the end of pl->watchers (gone since T966) */ +#endif + a_write(store, pl->attribs, pl); + WRITE_SECTION(store); + } +} + void write_alliances(struct gamedata *data) { alliance *al = alliances; while (al) { if (al->_leader) { - assert(al->_leader->_alive); WRITE_INT(data->store, al->id); WRITE_STR(data->store, al->name); WRITE_INT(data->store, (int)al->flags); @@ -463,7 +547,7 @@ void write_alliances(struct gamedata *data) } al = al->next; } - write_faction_reference(NULL, data->store); + WRITE_INT(data->store, 0); WRITE_SECTION(data->store); } @@ -514,18 +598,19 @@ static void read_owner(struct gamedata *data, region_owner ** powner) int id; READ_INT(data->store, &id); owner->last_owner = id ? findfaction(id) : NULL; - } else if (data->version >= OWNER_2_VERSION) { + } + else if (data->version >= OWNER_2_VERSION) { int id; alliance *a; READ_INT(data->store, &id); a = id ? findalliance(id) : NULL; /* don't know which faction, take the leader */ - owner->last_owner = a? a->_leader : NULL; + owner->last_owner = a ? a->_leader : NULL; } else { owner->last_owner = NULL; } - read_reference(owner, data->store, &read_faction_reference, &resolve_owner); + read_reference(owner, data, &read_faction_reference, &resolve_owner); *powner = owner; } else { @@ -542,8 +627,6 @@ static void write_owner(struct gamedata *data, region_owner * owner) WRITE_INT(data->store, owner->flags); f = owner->last_owner; write_faction_reference((f && f->_alive) ? f : NULL, data->store); - // TODO: check that destroyfaction does the right thing. - // TODO: What happens to morale when the owner dies? f = owner->owner; write_faction_reference((f && f->_alive) ? f : NULL, data->store); } @@ -558,7 +641,7 @@ int current_turn(void) int cturn = 0; FILE *F; - sprintf(zText, "%s/turn", basepath()); + join_path(basepath(), "turn", zText, sizeof(zText)); F = fopen(zText, "r"); if (!F) { perror(zText); @@ -575,7 +658,7 @@ int current_turn(void) static void writeorder(struct gamedata *data, const struct order *ord, -const struct locale *lang) + const struct locale *lang) { char obuf[1024]; write_order(ord, obuf, sizeof(obuf)); @@ -583,6 +666,36 @@ const struct locale *lang) WRITE_STR(data->store, obuf); } +int read_attribs(gamedata *data, attrib **alist, void *owner) { + int result; + if (data->version < ATHASH_VERSION) { + result = a_read_orig(data, alist, owner); + } + else { + result = a_read(data, alist, owner); + } + if (result == AT_READ_DEPR) { + /* handle deprecated attributes */ + attrib *a = *alist; + while (a) { + if (a->type->upgrade) { + a->type->upgrade(alist, a); + } + a = a->nexttype; + } + } + return result; +} + +void write_attribs(storage *store, attrib *alist, const void *owner) +{ +#if RELEASE_VERSION < ATHASH_VERSION + a_write_orig(store, alist, owner); +#else + a_write(store, alist, owner); +#endif +} + unit *read_unit(struct gamedata *data) { unit *u; @@ -618,8 +731,8 @@ unit *read_unit(struct gamedata *data) uhash(u); } - resolve_faction(read_faction_reference(data->store), &f); - assert(f); + READ_INT(data->store, &n); + f = findfaction(n); if (f != u->faction) { u_setfaction(u, f); } @@ -754,8 +867,7 @@ unit *read_unit(struct gamedata *data) log_error("Einheit %s hat %u Personen, und %u Trefferpunkte", itoa36(u->no), u->number, u->hp); u->hp = u->number; } - - a_read(data->store, &u->attribs, u); + read_attribs(data, &u->attribs, u); return u; } @@ -830,7 +942,7 @@ void write_unit(struct gamedata *data, const unit * u) } WRITE_INT(data->store, u->hp); WRITE_SECTION(data->store); - a_write(data->store, u->attribs, u); + write_attribs(data->store, u->attribs, u); WRITE_SECTION(data->store); } @@ -954,7 +1066,7 @@ static region *readregion(struct gamedata *data, int x, int y) rsetherbtype(r, NULL); } READ_INT(data->store, &n); - rsetherbs(r, (short)n); + rsetherbs(r, n); READ_INT(data->store, &n); if (n < 0) { /* bug 2182 */ @@ -985,11 +1097,11 @@ static region *readregion(struct gamedata *data, int x, int y) read_items(data->store, &r->land->items); if (data->version >= REGIONOWNER_VERSION) { READ_INT(data->store, &n); - region_set_morale(r, _max(0, (short) n), -1); + region_set_morale(r, _max(0, (short)n), -1); read_owner(data, &r->land->ownership); } } - a_read(data->store, &r->attribs, r); + read_attribs(data, &r->attribs, r); return r; } @@ -1008,7 +1120,7 @@ void writeregion(struct gamedata *data, const region * r) const item_type *rht; struct demand *demand; rawmaterial *res = r->resources; - + assert(r->land); WRITE_STR(data->store, (const char *)r->land->name); assert(rtrees(r, 0) >= 0); @@ -1053,7 +1165,7 @@ void writeregion(struct gamedata *data, const region * r) WRITE_SECTION(data->store); #endif } - a_write(data->store, r->attribs, r); + write_attribs(data->store, r->attribs, r); WRITE_SECTION(data->store); } @@ -1111,14 +1223,14 @@ int get_spell_level_faction(const spell * sp, void * cbdata) return 0; } -void read_spellbook(spellbook **bookp, struct storage *store, int(*get_level)(const spell * sp, void *), void * cbdata) +void read_spellbook(spellbook **bookp, gamedata *data, int(*get_level)(const spell * sp, void *), void * cbdata) { for (;;) { spell *sp = 0; char spname[64]; int level = 0; - READ_TOK(store, spname, sizeof(spname)); + READ_TOK(data->store, spname, sizeof(spname)); if (strcmp(spname, "end") == 0) break; if (bookp) { @@ -1127,8 +1239,8 @@ void read_spellbook(spellbook **bookp, struct storage *store, int(*get_level)(co log_error("read_spells: could not find spell '%s'", spname); } } - if (global.data_version >= SPELLBOOK_VERSION) { - READ_INT(store, &level); + if (data->version >= SPELLBOOK_VERSION) { + READ_INT(data->store, &level); } if (sp) { spellbook * sb = *bookp; @@ -1139,7 +1251,7 @@ void read_spellbook(spellbook **bookp, struct storage *store, int(*get_level)(co *bookp = create_spellbook(0); sb = *bookp; } - if (level>0 && (global.data_version >= SPELLBOOK_VERSION || !spellbook_get(sb, sp))) { + if (level > 0 && (data->version >= SPELLBOOK_VERSION || !spellbook_get(sb, sp))) { spellbook_add(sb, sp, level); } } @@ -1169,12 +1281,12 @@ static char * getpasswd(int fno) { if (F) { while (!feof(F)) { fgets(line, sizeof(line), F); - if (line[len]==':' && strncmp(prefix, line, len)==0) { - size_t slen = strlen(line)-1; - assert(line[slen]=='\n'); + if (line[len] == ':' && strncmp(prefix, line, len) == 0) { + size_t slen = strlen(line) - 1; + assert(line[slen] == '\n'); line[slen] = 0; fclose(F); - return _strdup(line+len+1); + return _strdup(line + len + 1); } } fclose(F); @@ -1220,14 +1332,15 @@ faction *readfaction(struct gamedata * data) { ally **sfp; int planes, n; - faction *f = NULL; + faction *f; char name[DISPLAYSIZE]; - variant var; - resolve_faction(var = read_faction_reference(data->store), &f); + READ_INT(data->store, &n); + assert(n > 0); + f = findfaction(n); if (f == NULL) { f = (faction *)calloc(1, sizeof(faction)); - f->no = var.i; + f->no = n; } else { f->allies = NULL; /* mem leak */ @@ -1309,7 +1422,7 @@ faction *readfaction(struct gamedata * data) } } - a_read(data->store, &f->attribs, f); + read_attribs(data, &f->attribs, f); read_items(data->store, &f->items); for (;;) { READ_TOK(data->store, name, sizeof(name)); @@ -1352,10 +1465,10 @@ faction *readfaction(struct gamedata * data) break; } } - read_groups(data->store, f); + read_groups(data, f); f->spellbook = 0; if (data->version >= REGIONOWNER_VERSION) { - read_spellbook(FactionSpells() ? &f->spellbook : 0, data->store, get_spell_level_faction, (void *)f); + read_spellbook(FactionSpells() ? &f->spellbook : 0, data, get_spell_level_faction, (void *)f); } return f; } @@ -1395,7 +1508,7 @@ void writefaction(struct gamedata *data, const faction * f) WRITE_INT(data->store, f->magiegebiet); WRITE_INT(data->store, f->flags & FFL_SAVEMASK); - a_write(data->store, f->attribs, f); + write_attribs(data->store, f->attribs, f); WRITE_SECTION(data->store); write_items(data->store, f->items); WRITE_SECTION(data->store); @@ -1412,18 +1525,20 @@ void writefaction(struct gamedata *data, const faction * f) WRITE_SECTION(data->store); for (sf = f->allies; sf; sf = sf->next) { - faction *fa = sf->faction; + int no; + int status; - assert(fa); - if (fa->_alive) { - int status = alliedfaction(NULL, f, fa, HELP_ALL); - if (status != 0) { - write_faction_reference(fa, data->store); - WRITE_INT(data->store, sf->status); - } + assert(sf->faction); + + no = sf->faction->no; + status = alliedfaction(NULL, f, sf->faction, HELP_ALL); + + if (status != 0) { + WRITE_INT(data->store, no); + WRITE_INT(data->store, sf->status); } } - write_faction_reference(NULL, data->store); + WRITE_INT(data->store, 0); WRITE_SECTION(data->store); write_groups(data->store, f); write_spellbook(f->spellbook, data->store); @@ -1439,16 +1554,8 @@ static int cb_sb_maxlevel(spellbook_entry *sbe, void *cbdata) { int readgame(const char *filename, bool backup) { - int n, p, nread; - faction *f, **fp; - region *r; - building *b, **bp; - ship **shp; - unit *u; - int rmax = maxregions; + int n; char path[MAX_PATH]; - char name[DISPLAYSIZE]; - const struct building_type *bt_lighthouse = bt_find("lighthouse"); gamedata gdata = { 0 }; storage store; stream strm; @@ -1457,7 +1564,7 @@ int readgame(const char *filename, bool backup) init_locales(); log_debug("- reading game data from %s", filename); - sprintf(path, "%s/%s", datapath(), filename); + join_path(datapath(), filename, path, sizeof(path)); if (backup) { create_backup(path); @@ -1469,29 +1576,44 @@ int readgame(const char *filename, bool backup) return -1; } sz = fread(&gdata.version, sizeof(int), 1, F); - if (sz!=sizeof(int) || gdata.version >= INTPAK_VERSION) { + if (sz != sizeof(int) || gdata.version >= INTPAK_VERSION) { int stream_version; size_t sz = fread(&stream_version, sizeof(int), 1, F); - assert((sz==1 && stream_version == STREAM_VERSION) || !"unsupported data format"); + assert((sz == 1 && stream_version == STREAM_VERSION) || !"unsupported data format"); } assert(gdata.version >= MIN_VERSION || !"unsupported data format"); assert(gdata.version <= MAX_VERSION || !"unsupported data format"); - gdata.encoding = enc_gamedata; fstream_init(&strm, F); binstore_init(&store, &strm); gdata.store = &store; - global.data_version = gdata.version; /* HACK: attribute::read does not have access to gamedata, only storage */ if (gdata.version >= BUILDNO_VERSION) { int build; READ_INT(&store, &build); log_debug("data in %s created with build %d.", filename, build); } - if (gdata.version >= SAVEGAMEID_VERSION) { + n = read_game(&gdata); + binstore_done(&store); + fstream_done(&strm); + return n; +} + +int read_game(gamedata *data) { + char name[DISPLAYSIZE]; + int n, p, nread; + faction *f, **fp; + region *r; + building *b, **bp; + ship **shp; + unit *u; + int rmax = maxregions; + const struct building_type *bt_lighthouse = bt_find("lighthouse"); + storage * store = data->store; + if (data->version >= SAVEGAMEID_VERSION) { int gameid; - READ_INT(&store, &gameid); + READ_INT(store, &gameid); if (gameid != game_id()) { int c; log_warning("game mismatch: datafile contains game %d, but config is for %d", gameid, game_id()); @@ -1504,81 +1626,26 @@ int readgame(const char *filename, bool backup) } } else { - READ_STR(&store, NULL, 0); + READ_STR(store, NULL, 0); } - a_read(&store, &global.attribs, NULL); - READ_INT(&store, &turn); + 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, &nread); /* max_unique_id = ignore */ - READ_INT(&store, &nextborder); + READ_INT(store, NULL); /* max_unique_id = ignore */ + READ_INT(store, &nextborder); - /* Planes */ - planes = NULL; - READ_INT(&store, &nread); - while (--nread >= 0) { - int id; - plane *pl; - - READ_INT(&store, &id); - pl = getplanebyid(id); - - if (pl == NULL) { - pl = calloc(1, sizeof(plane)); - } - else { - log_warning("the plane with id=%d already exists.", id); - } - pl->id = id; - READ_STR(&store, name, sizeof(name)); - pl->name = _strdup(name); - READ_INT(&store, &pl->minx); - READ_INT(&store, &pl->maxx); - READ_INT(&store, &pl->miny); - READ_INT(&store, &pl->maxy); - READ_INT(&store, &pl->flags); - - /* read watchers */ - if (gdata.version < FIX_WATCHERS_VERSION) { - char rname[64]; - /* before this version, watcher storage was pretty broken. we are incompatible and don't read them */ - for (;;) { - READ_TOK(&store, rname, sizeof(rname)); - if (strcmp(rname, "end") == 0) { - break; /* this is most likely the end of the list */ - } - else { - log_error( - ("This datafile contains watchers, but we are unable to read them.")); - } - } - } - else { - /* WATCHERS - eliminated in February 2016, ca. turn 966 */ - if (gdata.version < CRYPT_VERSION) { - variant fno; - do { - fno = read_faction_reference(&store); - } while (fno.i); - } - } - a_read(&store, &pl->attribs, pl); - if (pl->id != 1094969858) { // Regatta - addlist(&planes, pl); - } - } - - /* Read factions */ - read_alliances(&store); - READ_INT(&store, &nread); + read_planes(data); + read_alliances(data); + READ_INT(store, &nread); log_debug(" - Einzulesende Parteien: %d\n", nread); fp = &factions; while (*fp) fp = &(*fp)->next; while (--nread >= 0) { - faction *f = readfaction(&gdata); + faction *f = readfaction(data); *fp = f; fp = &f->next; @@ -1588,8 +1655,8 @@ int readgame(const char *filename, bool backup) /* Regionen */ - READ_INT(&store, &nread); - assert(nread < MAXREGIONS); + READ_INT(store, &nread); + assert(nread < MAXREGIONS && nread>=0); if (rmax < 0) { rmax = nread; } @@ -1597,67 +1664,73 @@ int readgame(const char *filename, bool backup) while (--nread >= 0) { unit **up; int x, y; - READ_INT(&store, &x); - READ_INT(&store, &y); + READ_INT(store, &x); + READ_INT(store, &y); if ((nread & 0x3FF) == 0) { /* das spart extrem Zeit */ log_debug(" - Einzulesende Regionen: %d/%d * %d,%d \r", rmax, nread, x, y); } --rmax; - r = readregion(&gdata, x, y); + r = readregion(data, x, y); /* Burgen */ - READ_INT(&store, &p); + READ_INT(store, &p); bp = &r->buildings; while (--p >= 0) { b = (building *)calloc(1, sizeof(building)); - READ_INT(&store, &b->no); + READ_INT(store, &b->no); *bp = b; bp = &b->next; bhash(b); - READ_STR(&store, name, sizeof(name)); + READ_STR(store, name, sizeof(name)); b->name = _strdup(name); if (lomem) { - READ_STR(gdata.store, NULL, 0); + READ_STR(store, NULL, 0); } else { - READ_STR(&store, name, sizeof(name)); + READ_STR(store, name, sizeof(name)); b->display = _strdup(name); } - READ_INT(&store, &b->size); - READ_STR(&store, name, sizeof(name)); + READ_INT(store, &b->size); + READ_STR(store, name, sizeof(name)); b->type = bt_find(name); b->region = r; - a_read(&store, &b->attribs, b); + read_attribs(data, &b->attribs, b); if (b->type == bt_lighthouse) { r->flags |= RF_LIGHTHOUSE; } + + // repairs, bug 2221: + if (b->type->maxsize>0 && b->size>b->type->maxsize) { + log_error("building too big: %s (%s size %d of %d), fixing.", buildingname(b), b->type->_name, b->size, b->type->maxsize); + b->size = b->type->maxsize; + } } /* Schiffe */ - READ_INT(&store, &p); + READ_INT(store, &p); shp = &r->ships; while (--p >= 0) { ship *sh = (ship *)calloc(1, sizeof(ship)); sh->region = r; - READ_INT(&store, &sh->no); + READ_INT(store, &sh->no); *shp = sh; shp = &sh->next; shash(sh); - READ_STR(&store, name, sizeof(name)); + READ_STR(store, name, sizeof(name)); sh->name = _strdup(name); if (lomem) { - READ_STR(&store, NULL, 0); + READ_STR(store, NULL, 0); } else { - READ_STR(&store, name, sizeof(name)); + READ_STR(store, name, sizeof(name)); sh->display = _strdup(name); } - READ_STR(&store, name, sizeof(name)); + READ_STR(store, name, sizeof(name)); sh->type = st_find(name); if (sh->type == NULL) { /* old datafiles */ @@ -1665,33 +1738,33 @@ int readgame(const char *filename, bool backup) } assert(sh->type || !"ship_type not registered!"); - READ_INT(&store, &sh->size); - READ_INT(&store, &sh->damage); - if (gdata.version >= FOSS_VERSION) { - READ_INT(&store, &sh->flags); + READ_INT(store, &sh->size); + READ_INT(store, &sh->damage); + if (data->version >= FOSS_VERSION) { + READ_INT(store, &sh->flags); } /* Attribute rekursiv einlesen */ - READ_INT(&store, &n); + READ_INT(store, &n); sh->coast = (direction_t)n; if (sh->type->flags & SFL_NOCOAST) { sh->coast = NODIRECTION; } - a_read(&store, &sh->attribs, sh); + read_attribs(data, &sh->attribs, sh); } *shp = 0; /* Einheiten */ - READ_INT(&store, &p); + READ_INT(store, &p); up = &r->units; while (--p >= 0) { - unit *u = read_unit(&gdata); + unit *u = read_unit(data); - if (gdata.version < JSON_REPORT_VERSION) { + if (data->version < JSON_REPORT_VERSION) { if (u->_name && fval(u->faction, FFL_NPC)) { if (!u->_name[0] || unit_name_equals_race(u)) { unit_setname(u, NULL); @@ -1706,10 +1779,8 @@ int readgame(const char *filename, bool backup) update_interval(u->faction, u->region); } } - read_borders(&store); + read_borders(data); - binstore_done(&store); - fstream_done(&strm); /* Unaufgeloeste Zeiger initialisieren */ log_debug("fixing unresolved references."); resolve(); @@ -1737,7 +1808,7 @@ int readgame(const char *filename, bool backup) } else { for (u = f->units; u; u = u->nextF) { - if (global.data_version < SPELL_LEVEL_VERSION) { + if (data->version < SPELL_LEVEL_VERSION) { sc_mage *mage = get_mage(u); if (mage) { faction *f = u->faction; @@ -1756,12 +1827,12 @@ int readgame(const char *filename, bool backup) } if (u->number > 0) { f->_alive = true; - if (global.data_version >= SPELL_LEVEL_VERSION) { + if (data->version >= SPELL_LEVEL_VERSION) { break; } } } - if (global.data_version < SPELL_LEVEL_VERSION && f->spellbook) { + if (data->version < SPELL_LEVEL_VERSION && f->spellbook) { spellbook_foreach(f->spellbook, cb_sb_maxlevel, f); } } @@ -1787,12 +1858,6 @@ static void clear_npc_orders(faction *f) int writegame(const char *filename) { int n; - faction *f; - region *r; - building *b; - ship *sh; - unit *u; - plane *pl; char path[MAX_PATH]; gamedata gdata; storage store; @@ -1812,177 +1877,123 @@ int writegame(const char *filename) } gdata.store = &store; - gdata.encoding = enc_gamedata; gdata.version = RELEASE_VERSION; - global.data_version = RELEASE_VERSION; - n = STREAM_VERSION; fwrite(&gdata.version, sizeof(int), 1, F); + n = STREAM_VERSION; fwrite(&n, sizeof(int), 1, F); fstream_init(&strm, F); binstore_init(&store, &strm); - /* globale Variablen */ - WRITE_INT(&store, VERSION_BUILD); - WRITE_INT(&store, game_id()); - WRITE_SECTION(&store); + n = write_game(&gdata); + binstore_done(&store); + fstream_done(&strm); + return n; +} - a_write(&store, global.attribs, NULL); - WRITE_SECTION(&store); +int write_game(gamedata *data) { + storage * store = data->store; + region *r; + faction *f; + int n; - WRITE_INT(&store, turn); - WRITE_INT(&store, 0 /*max_unique_id */); - WRITE_INT(&store, nextborder); + /* globale Variablen */ + assert(data->version <= MAX_VERSION && data->version >= MIN_VERSION); - /* Write planes */ - WRITE_SECTION(&store); - WRITE_INT(&store, listlen(planes)); - WRITE_SECTION(&store); + WRITE_INT(store, game_id()); + WRITE_SECTION(store); - for (pl = planes; pl; pl = pl->next) { - WRITE_INT(&store, pl->id); - WRITE_STR(&store, pl->name); - WRITE_INT(&store, pl->minx); - WRITE_INT(&store, pl->maxx); - WRITE_INT(&store, pl->miny); - WRITE_INT(&store, pl->maxy); - WRITE_INT(&store, pl->flags); -#if RELEASE_VERSION < CRYPT_VERSION - write_faction_reference(NULL, &store); /* mark the end of pl->watchers (gone since T966) */ -#endif - a_write(&store, pl->attribs, pl); - WRITE_SECTION(&store); - } + write_attribs(store, global.attribs, NULL); + WRITE_SECTION(store); - /* Write factions */ - write_alliances(&gdata); + WRITE_INT(store, turn); + WRITE_INT(store, 0 /* max_unique_id */); + WRITE_INT(store, nextborder); + + write_planes(store); + write_alliances(data); n = listlen(factions); - WRITE_INT(&store, n); - WRITE_SECTION(&store); + WRITE_INT(store, n); + WRITE_SECTION(store); log_debug(" - Schreibe %d Parteien...", n); for (f = factions; f; f = f->next) { if (fval(f, FFL_NPC)) { clear_npc_orders(f); } - writefaction(&gdata, f); - WRITE_SECTION(&store); + writefaction(data, f); + WRITE_SECTION(store); } /* Write regions */ n = listlen(regions); - WRITE_INT(&store, n); - WRITE_SECTION(&store); + WRITE_INT(store, n); + WRITE_SECTION(store); log_debug(" - Schreibe Regionen: %d", n); for (r = regions; r; r = r->next, --n) { + ship *sh; + building *b; + unit *u; /* plus leerzeile */ if ((n % 1024) == 0) { /* das spart extrem Zeit */ log_debug(" - Schreibe Regionen: %d", n); } - WRITE_SECTION(&store); - WRITE_INT(&store, r->x); - WRITE_INT(&store, r->y); - writeregion(&gdata, r); + WRITE_SECTION(store); + WRITE_INT(store, r->x); + WRITE_INT(store, r->y); + writeregion(data, r); - WRITE_INT(&store, listlen(r->buildings)); - WRITE_SECTION(&store); + WRITE_INT(store, listlen(r->buildings)); + WRITE_SECTION(store); for (b = r->buildings; b; b = b->next) { - write_building_reference(b, &store); - WRITE_STR(&store, b->name); - WRITE_STR(&store, b->display ? b->display : ""); - WRITE_INT(&store, b->size); - WRITE_TOK(&store, b->type->_name); - WRITE_SECTION(&store); - a_write(&store, b->attribs, b); - WRITE_SECTION(&store); + write_building_reference(b, store); + WRITE_STR(store, b->name); + WRITE_STR(store, b->display ? b->display : ""); + WRITE_INT(store, b->size); + WRITE_TOK(store, b->type->_name); + WRITE_SECTION(store); + write_attribs(store, b->attribs, b); + WRITE_SECTION(store); } - WRITE_INT(&store, listlen(r->ships)); - WRITE_SECTION(&store); + WRITE_INT(store, listlen(r->ships)); + WRITE_SECTION(store); for (sh = r->ships; sh; sh = sh->next) { assert(sh->region == r); - write_ship_reference(sh, &store); - WRITE_STR(&store, (const char *)sh->name); - WRITE_STR(&store, sh->display ? (const char *)sh->display : ""); - WRITE_TOK(&store, sh->type->_name); - WRITE_INT(&store, sh->size); - WRITE_INT(&store, sh->damage); - WRITE_INT(&store, sh->flags & SFL_SAVEMASK); + write_ship_reference(sh, store); + WRITE_STR(store, (const char *)sh->name); + WRITE_STR(store, sh->display ? (const char *)sh->display : ""); + WRITE_TOK(store, sh->type->_name); + WRITE_INT(store, sh->size); + WRITE_INT(store, sh->damage); + WRITE_INT(store, sh->flags & SFL_SAVEMASK); assert((sh->type->flags & SFL_NOCOAST) == 0 || sh->coast == NODIRECTION); - WRITE_INT(&store, sh->coast); - WRITE_SECTION(&store); - a_write(&store, sh->attribs, sh); - WRITE_SECTION(&store); + WRITE_INT(store, sh->coast); + WRITE_SECTION(store); + write_attribs(store, sh->attribs, sh); + WRITE_SECTION(store); } - WRITE_INT(&store, listlen(r->units)); - WRITE_SECTION(&store); + WRITE_INT(store, listlen(r->units)); + WRITE_SECTION(store); for (u = r->units; u; u = u->next) { - write_unit(&gdata, u); + write_unit(data, u); } } - WRITE_SECTION(&store); - write_borders(&store); - WRITE_SECTION(&store); - - binstore_done(&store); - fstream_done(&strm); + WRITE_SECTION(store); + write_borders(store); + WRITE_SECTION(store); return 0; } -void gamedata_close(gamedata *data) { - binstore_done(data->store); - fstream_done(&data->strm); -} - -gamedata *gamedata_open(const char *filename, const char *mode) { - FILE *F = fopen(filename, mode); - - if (F) { - gamedata *data = (gamedata *)calloc(1, sizeof(gamedata)); - storage *store = (storage *)calloc(1, sizeof(storage)); - int err = 0; - size_t sz; - - data->store = store; - if (strchr(mode, 'r')) { - sz = fread(&data->version, 1, sizeof(int), F); - if (sz != sizeof(int)) { - err = ferror(F); - } - else { - err = fseek(F, sizeof(int), SEEK_CUR); - } - } - else if (strchr(mode, 'w')) { - int n = STREAM_VERSION; - data->version = RELEASE_VERSION; - fwrite(&data->version, sizeof(int), 1, F); - fwrite(&n, sizeof(int), 1, F); - } - if (err) { - fclose(F); - free(data); - free(store); - } - else { - fstream_init(&data->strm, F); - binstore_init(store, &data->strm); - return data; - } - } - log_error("could not open %s: %s", filename, strerror(errno)); - return 0; -} - -int a_readint(attrib * a, void *owner, struct storage *store) +int a_readint(attrib * a, void *owner, struct gamedata *data) { /* assert(sizeof(int)==sizeof(a->data)); */ - READ_INT(store, &a->data.i); + READ_INT(data->store, &a->data.i); return AT_READ_OK; } @@ -1991,12 +2002,12 @@ void a_writeint(const attrib * a, const void *owner, struct storage *store) WRITE_INT(store, a->data.i); } -int a_readshorts(attrib * a, void *owner, struct storage *store) +int a_readshorts(attrib * a, void *owner, struct gamedata *data) { int n; - READ_INT(store, &n); + READ_INT(data->store, &n); a->data.sa[0] = (short)n; - READ_INT(store, &n); + READ_INT(data->store, &n); a->data.sa[1] = (short)n; return AT_READ_OK; } @@ -2007,12 +2018,12 @@ void a_writeshorts(const attrib * a, const void *owner, struct storage *store) WRITE_INT(store, a->data.sa[1]); } -int a_readchars(attrib * a, void *owner, struct storage *store) +int a_readchars(attrib * a, void *owner, struct gamedata *data) { int i; for (i = 0; i != 4; ++i) { int n; - READ_INT(store, &n); + READ_INT(data->store, &n); a->data.ca[i] = (char)n; } return AT_READ_OK; @@ -2027,7 +2038,7 @@ void a_writechars(const attrib * a, const void *owner, struct storage *store) } } -int a_readvoid(attrib * a, void *owner, struct storage *store) +int a_readvoid(attrib * a, void *owner, struct gamedata *data) { return AT_READ_OK; } @@ -2036,14 +2047,14 @@ void a_writevoid(const attrib * a, const void *owner, struct storage *store) { } -int a_readstring(attrib * a, void *owner, struct storage *store) +int a_readstring(attrib * a, void *owner, struct gamedata *data) { char buf[DISPLAYSIZE]; char * result = 0; int e; size_t len = 0; do { - e = READ_STR(store, buf, sizeof(buf)); + e = READ_STR(data->store, buf, sizeof(buf)); if (result) { result = realloc(result, len + DISPLAYSIZE - 1); strcpy(result + len, buf); diff --git a/src/kernel/save.h b/src/kernel/save.h index cedd5befc..3cd443926 100644 --- a/src/kernel/save.h +++ b/src/kernel/save.h @@ -30,13 +30,7 @@ extern "C" { struct spell; struct spellbook; struct unit; - - typedef struct gamedata { - struct storage *store; - stream strm; - int version; - int encoding; - } gamedata; + struct gamedata; #define MAX_INPUT_SIZE DISPLAYSIZE*2 /* Nach MAX_INPUT_SIZE brechen wir das Einlesen der Zeile ab und nehmen an, @@ -55,33 +49,36 @@ extern "C" { void read_items(struct storage *store, struct item **it); void write_items(struct storage *store, struct item *it); - void read_spellbook(struct spellbook **bookp, struct storage *store, int(*get_level)(const struct spell * sp, void *), void * cbdata); + void read_spellbook(struct spellbook **bookp, struct gamedata *data, int(*get_level)(const struct spell * sp, void *), void * cbdata); void write_spellbook(const struct spellbook *book, struct storage *store); + void write_attribs(struct storage *store, struct attrib *alist, const void *owner); + int read_attribs(struct gamedata *store, struct attrib **alist, void *owner); + void write_unit(struct gamedata *data, const struct unit *u); struct unit *read_unit(struct gamedata *data); - int a_readint(struct attrib *a, void *owner, struct storage *store); + int a_readint(struct attrib *a, void *owner, struct gamedata *); void a_writeint(const struct attrib *a, const void *owner, struct storage *store); - int a_readshorts(struct attrib *a, void *owner, struct storage *store); + int a_readshorts(struct attrib *a, void *owner, struct gamedata *); void a_writeshorts(const struct attrib *a, const void *owner, struct storage *store); - int a_readchars(struct attrib *a, void *owner, struct storage *store); + int a_readchars(struct attrib *a, void *owner, struct gamedata *); void a_writechars(const struct attrib *a, const void *owner, struct storage *store); - int a_readvoid(struct attrib *a, void *owner, struct storage *store); + int a_readvoid(struct attrib *a, void *owner, struct gamedata *); void a_writevoid(const struct attrib *a, const void *owner, - struct storage *store); - int a_readstring(struct attrib *a, void *owner, struct storage *store); + struct storage *); + int a_readstring(struct attrib *a, void *owner, struct gamedata *); void a_writestring(const struct attrib *a, const void *owner, - struct storage *store); + struct storage *); void a_finalizestring(struct attrib *a); void create_backup(char *file); - struct gamedata *gamedata_open(const char *filename, const char *mode); - void gamedata_close(struct gamedata *data); + int write_game(struct gamedata *data); + int read_game(struct gamedata *data); /* test-only functions that give access to internal implementation details (BAD) */ void _test_write_password(struct gamedata *data, const struct faction *f); diff --git a/src/kernel/save.test.c b/src/kernel/save.test.c index 070ee878e..e88cf7b2b 100644 --- a/src/kernel/save.test.c +++ b/src/kernel/save.test.c @@ -1,5 +1,8 @@ #include #include +#include +#include +#include #include "save.h" #include "unit.h" @@ -9,6 +12,7 @@ #include "plane.h" #include "region.h" #include "version.h" + #include #include #include @@ -17,6 +21,8 @@ #include #include +#include +#include #include #include @@ -29,51 +35,69 @@ static void test_readwrite_data(CuTest * tc) const char *filename = "test.dat"; char path[MAX_PATH]; test_cleanup(); - sprintf(path, "%s/%s", datapath(), filename); CuAssertIntEquals(tc, 0, writegame(filename)); CuAssertIntEquals(tc, 0, readgame(filename, false)); - CuAssertIntEquals(tc, RELEASE_VERSION, global.data_version); + join_path(datapath(), filename, path, sizeof(path)); CuAssertIntEquals(tc, 0, remove(path)); test_cleanup(); } static void test_readwrite_unit(CuTest * tc) { - const char *filename = "test.dat"; - char path[MAX_PATH]; - gamedata *data; + gamedata data; + storage store; struct unit *u; struct region *r; struct faction *f; int fno; - /* FIXME: at some point during this test, errno is set to 17 (File exists), why? */ - create_directories(); test_cleanup(); r = test_create_region(0, 0, 0); f = test_create_faction(0); fno = f->no; u = test_create_unit(f, r); - join_path(datapath(), filename, path, sizeof(path)); - - data = gamedata_open(path, "wb"); - CuAssertPtrNotNull(tc, data); - - write_unit(data, u); - gamedata_close(data); + mstream_init(&data.strm); + gamedata_init(&data, &store, RELEASE_VERSION); + write_unit(&data, u); + + data.strm.api->rewind(data.strm.handle); free_gamedata(); f = test_create_faction(0); renumber_faction(f, fno); - data = gamedata_open(path, "rb"); - CuAssertPtrNotNull(tc, data); - u = read_unit(data); - gamedata_close(data); + gamedata_init(&data, &store, RELEASE_VERSION); + u = read_unit(&data); + mstream_done(&data.strm); + gamedata_done(&data); CuAssertPtrNotNull(tc, u); CuAssertPtrEquals(tc, f, u->faction); CuAssertPtrEquals(tc, 0, u->region); - CuAssertIntEquals(tc, 0, remove(path)); + test_cleanup(); +} + +static void test_readwrite_attrib(CuTest *tc) { + gamedata data; + storage store; + attrib *a = NULL; + + test_cleanup(); + key_set(&a, 41); + key_set(&a, 42); + mstream_init(&data.strm); + gamedata_init(&data, &store, RELEASE_VERSION); + write_attribs(data.store, a, NULL); + a_removeall(&a, NULL); + CuAssertPtrEquals(tc, 0, a); + + data.strm.api->rewind(data.strm.handle); + read_attribs(&data, &a, NULL); + mstream_done(&data.strm); + gamedata_done(&data); + CuAssertTrue(tc, key_get(a, 41)); + CuAssertTrue(tc, key_get(a, 42)); + a_removeall(&a, NULL); + test_cleanup(); } @@ -83,6 +107,11 @@ static void test_readwrite_dead_faction_group(CuTest *tc) { group *g; ally *al; int fno; + gamedata data; + storage store; + + mstream_init(&data.strm); + gamedata_init(&data, &store, RELEASE_VERSION); test_cleanup(); f = test_create_faction(0); @@ -102,10 +131,11 @@ static void test_readwrite_dead_faction_group(CuTest *tc) { destroyfaction(&factions); CuAssertTrue(tc, !f->_alive); CuAssertPtrEquals(tc, f2, factions); - writegame("test.dat"); + write_game(&data); free_gamedata(); f = f2 = NULL; - readgame("test.dat", false); + data.strm.api->rewind(data.strm.handle); + read_game(&data); CuAssertPtrEquals(tc, 0, findfaction(fno)); f2 = factions; CuAssertPtrNotNull(tc, f2); @@ -114,12 +144,19 @@ static void test_readwrite_dead_faction_group(CuTest *tc) { g = get_group(u); CuAssertPtrNotNull(tc, g); CuAssertPtrEquals(tc, 0, g->allies); + mstream_done(&data.strm); + gamedata_done(&data); test_cleanup(); } static void test_readwrite_dead_faction_regionowner(CuTest *tc) { faction *f; region *r; + gamedata data; + storage store; + + mstream_init(&data.strm); + gamedata_init(&data, &store, RELEASE_VERSION); test_cleanup(); config_set("rules.region_owners", "1"); @@ -129,10 +166,13 @@ static void test_readwrite_dead_faction_regionowner(CuTest *tc) { destroyfaction(&factions); CuAssertTrue(tc, !f->_alive); remove_empty_units(); - writegame("test.dat"); + write_game(&data); free_gamedata(); f = NULL; - readgame("test.dat", false); + data.strm.api->rewind(data.strm.handle); + read_game(&data); + mstream_done(&data.strm); + gamedata_done(&data); f = factions; CuAssertPtrEquals(tc, 0, f); r = regions; @@ -142,6 +182,8 @@ static void test_readwrite_dead_faction_regionowner(CuTest *tc) { } static void test_readwrite_dead_faction_changefaction(CuTest *tc) { + gamedata data; + storage store; faction *f, *f2; region *r; trigger *tr; @@ -157,10 +199,15 @@ static void test_readwrite_dead_faction_changefaction(CuTest *tc) { destroyfaction(&factions); CuAssertTrue(tc, !f->_alive); remove_empty_units(); - writegame("test.dat"); + mstream_init(&data.strm); + gamedata_init(&data, &store, RELEASE_VERSION); + write_game(&data); free_gamedata(); f = NULL; - readgame("test.dat", false); + data.strm.api->rewind(data.strm.handle); + read_game(&data); + mstream_done(&data.strm); + gamedata_done(&data); f = factions; CuAssertPtrNotNull(tc, f); r = regions; @@ -172,6 +219,8 @@ static void test_readwrite_dead_faction_changefaction(CuTest *tc) { } static void test_readwrite_dead_faction_createunit(CuTest *tc) { + gamedata data; + storage store; faction *f, *f2; region *r; trigger *tr; @@ -187,10 +236,15 @@ static void test_readwrite_dead_faction_createunit(CuTest *tc) { destroyfaction(&factions); CuAssertTrue(tc, !f->_alive); remove_empty_units(); - writegame("test.dat"); + mstream_init(&data.strm); + gamedata_init(&data, &store, RELEASE_VERSION); + write_game(&data); free_gamedata(); f = NULL; - readgame("test.dat", false); + data.strm.api->rewind(data.strm.handle); + read_game(&data); + mstream_done(&data.strm); + gamedata_done(&data); f = factions; CuAssertPtrNotNull(tc, f); r = regions; @@ -202,26 +256,26 @@ static void test_readwrite_dead_faction_createunit(CuTest *tc) { } static void test_read_password(CuTest *tc) { - const char *path = "test.dat"; - gamedata *data; + gamedata data; + storage store; faction *f; + f = test_create_faction(0); faction_setpassword(f, password_encode("secret", PASSWORD_DEFAULT)); - data = gamedata_open(path, "wb"); - CuAssertPtrNotNull(tc, data); - _test_write_password(data, f); - gamedata_close(data); - data = gamedata_open(path, "rb"); - CuAssertPtrNotNull(tc, data); - _test_read_password(data, f); - gamedata_close(data); + mstream_init(&data.strm); + gamedata_init(&data, &store, RELEASE_VERSION); + _test_write_password(&data, f); + data.strm.api->rewind(data.strm.handle); + _test_read_password(&data, f); + mstream_done(&data.strm); + gamedata_done(&data); CuAssertTrue(tc, checkpasswd(f, "secret")); - CuAssertIntEquals(tc, 0, remove(path)); } static void test_read_password_external(CuTest *tc) { - const char *path = "test.dat", *pwfile = "passwords.txt"; - gamedata *data; + gamedata data; + storage store; + const char *pwfile = "passwords.txt"; faction *f; FILE * F; @@ -229,40 +283,40 @@ static void test_read_password_external(CuTest *tc) { f = test_create_faction(0); faction_setpassword(f, password_encode("secret", PASSWORD_DEFAULT)); CuAssertPtrNotNull(tc, f->_password); - data = gamedata_open(path, "wb"); - CuAssertPtrNotNull(tc, data); - WRITE_TOK(data->store, "newpassword"); - WRITE_TOK(data->store, "secret"); - WRITE_TOK(data->store, "$brokenhash"); - gamedata_close(data); - data = gamedata_open(path, "rb"); - CuAssertPtrNotNull(tc, data); - data->version = NOCRYPT_VERSION; - _test_read_password(data, f); + mstream_init(&data.strm); + gamedata_init(&data, &store, RELEASE_VERSION); + WRITE_TOK(data.store, "newpassword"); + WRITE_TOK(data.store, "secret"); + WRITE_TOK(data.store, "$brokenhash"); + data.strm.api->rewind(data.strm.handle); + data.version = NOCRYPT_VERSION; + _test_read_password(&data, f); CuAssertStrEquals(tc, "newpassword", f->_password); - data->version = BADCRYPT_VERSION; - _test_read_password(data, f); + data.version = BADCRYPT_VERSION; + _test_read_password(&data, f); CuAssertStrEquals(tc, "secret", f->_password); F = fopen(pwfile, "wt"); fprintf(F, "%s:pwfile\n", itoa36(f->no)); fclose(F); - _test_read_password(data, f); + CuAssertTrue(tc, checkpasswd(f, "secret")); + _test_read_password(&data, f); CuAssertStrEquals(tc, "pwfile", f->_password); CuAssertTrue(tc, checkpasswd(f, "pwfile")); - gamedata_close(data); - CuAssertIntEquals(tc, 0, remove(path)); + mstream_done(&data.strm); + gamedata_done(&data); CuAssertIntEquals(tc, 0, remove(pwfile)); } CuSuite *get_save_suite(void) { CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_readwrite_attrib); SUITE_ADD_TEST(suite, test_readwrite_data); SUITE_ADD_TEST(suite, test_readwrite_unit); SUITE_ADD_TEST(suite, test_readwrite_dead_faction_createunit); SUITE_ADD_TEST(suite, test_readwrite_dead_faction_changefaction); SUITE_ADD_TEST(suite, test_readwrite_dead_faction_regionowner); - DISABLE_TEST(suite, test_readwrite_dead_faction_group); + SUITE_ADD_TEST(suite, test_readwrite_dead_faction_group); SUITE_ADD_TEST(suite, test_read_password); SUITE_ADD_TEST(suite, test_read_password_external); return suite; diff --git a/src/kernel/skills.c b/src/kernel/skills.c index 7ba325dcc..171d42dd0 100644 --- a/src/kernel/skills.c +++ b/src/kernel/skills.c @@ -59,6 +59,7 @@ attrib_type at_skillmod = { NULL, NULL, /* can't write function pointers */ NULL, /* can't read function pointers */ + NULL, ATF_PRESERVE }; @@ -205,7 +206,7 @@ int level(int days) void sk_set(skill * sv, int level) { - assert(level != 0); + assert(sv && level != 0); sv->weeks = skill_weeks(level); sv->level = level; } diff --git a/src/kernel/spell.c b/src/kernel/spell.c index ebd5d44ef..ec651bd79 100644 --- a/src/kernel/spell.c +++ b/src/kernel/spell.c @@ -36,8 +36,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. static critbit_tree cb_spells; quicklist * spells; -static void free_spell_cb(void *cbdata) { - spell *sp = (spell *)cbdata; +static void free_spell(spell *sp) { free(sp->syntax); free(sp->parameter); free(sp->sname); @@ -45,6 +44,10 @@ static void free_spell_cb(void *cbdata) { free(sp); } +static void free_spell_cb(void *cbdata) { + free_spell((spell *)cbdata); +} + void free_spells(void) { cb_clear(&cb_spells); ql_foreach(spells, free_spell_cb); diff --git a/src/kernel/spell.h b/src/kernel/spell.h index a69b61989..b447f51d4 100644 --- a/src/kernel/spell.h +++ b/src/kernel/spell.h @@ -45,9 +45,6 @@ extern "C" { fumble_f fumble; } spell; - int use_item_power(struct region *r, struct unit *u); - int use_item_regeneration(struct region *r, struct unit *u); - void showspells(struct region *r, struct unit *u); int sp_antimagiczone(struct castorder *co); struct spell * create_spell(const char * name, unsigned int id); diff --git a/src/kernel/terrain.h b/src/kernel/terrain.h index 931a74861..3d1491867 100644 --- a/src/kernel/terrain.h +++ b/src/kernel/terrain.h @@ -31,7 +31,6 @@ extern "C" { #define CAVALRY_REGION (1<<4) /* riding in combat is possible */ /* Achtung: SEA_REGION ist nicht das Gegenteil von LAND_REGION. Die zwei schliessen sich nichtmal aus! */ #define FORBIDDEN_REGION (1<<5) /* unpassierbare Blockade-struct region */ -#define SAIL_INTO (1<<6) /* man darf hierhin segeln */ #define FLY_INTO (1<<7) /* man darf hierhin fliegen */ #define SWIM_INTO (1<<8) /* man darf hierhin schwimmen */ #define WALK_INTO (1<<9) /* man darf hierhin laufen */ diff --git a/src/kernel/unit.c b/src/kernel/unit.c index e4f0b1d44..47e4a63b4 100644 --- a/src/kernel/unit.c +++ b/src/kernel/unit.c @@ -28,7 +28,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "curse.h" #include "item.h" #include "move.h" -#include "monster.h" #include "order.h" #include "plane.h" #include "race.h" @@ -52,6 +51,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include #include #include @@ -116,9 +116,9 @@ unit *findunitr(const region * r, int n) { unit *u; /* findunit regional! */ - assert(n>0); + assert(n > 0); u = ufindhash(n); - return (u && u->region==r)?u:0; + return (u && u->region == r) ? u : 0; } // TODO: deprecated, replace with findunit(n) @@ -443,8 +443,9 @@ int ualias(const unit * u) return a->data.i; } -int a_readprivate(attrib * a, void *owner, struct storage *store) +int a_readprivate(attrib * a, void *owner, gamedata *data) { + struct storage *store = data->store; char lbuf[DISPLAYSIZE]; READ_STR(store, lbuf, sizeof(lbuf)); a->data.v = _strdup(lbuf); @@ -579,9 +580,9 @@ void a_writesiege(const attrib * a, const void *owner, struct storage *store) write_building_reference(b, store); } -int a_readsiege(attrib * a, void *owner, struct storage *store) +int a_readsiege(attrib * a, void *owner, gamedata *data) { - int result = read_reference(&a->data.v, store, read_building_reference, + int result = read_reference(&a->data.v, data, read_building_reference, resolve_building); if (result == 0 && !a->data.v) { return AT_READ_FAIL; @@ -656,7 +657,7 @@ bool ucontact(const unit * u, const unit * u2) /* Explizites KONTAKTIERE */ for (ru = a_find(u->attribs, &at_contact); ru && ru->type == &at_contact; - ru = ru->next) { + ru = ru->next) { if (((unit *)ru->data.v) == u2) { return true; } @@ -698,10 +699,10 @@ int resolve_unit(variant id, void *address) return 0; } -variant read_unit_reference(struct storage * store) +variant read_unit_reference(gamedata *data) { variant var; - READ_INT(store, &var.i); + READ_INT(data->store, &var.i); return var; } @@ -781,7 +782,7 @@ ship *leftship(const unit * u) void u_set_building(unit * u, building * b) { - assert(!u->building); /* you must leave first */ + assert(!b || !u->building); /* you must leave first */ u->building = b; if (b && (!b->_owner || b->_owner->number <= 0)) { building_set_owner(u); @@ -830,7 +831,7 @@ bool can_leave(unit * u) rule_leave = config_get_int("rules.move.owner_leave", 0); - if (rule_leave!=0 && u->building && u == building_owner(u->building)) { + if (rule_leave != 0 && u->building && u == building_owner(u->building)) { return false; } return true; @@ -1001,7 +1002,7 @@ void transfermen(unit * u, unit * dst, int n) set_leftship(dst, sh); dst->flags |= u->flags & (UFL_LONGACTION | UFL_NOTMOVING | UFL_HUNGER | UFL_MOVED | - UFL_ENTER); + UFL_ENTER); if (u->attribs) { transfer_curse(u, dst, n); } @@ -1043,7 +1044,7 @@ struct building *inside_building(const struct unit *u) if (!u->building) { return NULL; } - else if (u->building->size < u->building->type->maxsize) { + else if (!building_finished(u->building)) { /* Gebaeude noch nicht fertig */ return NULL; } @@ -1128,30 +1129,6 @@ void set_number(unit * u, int count) u->number = (unsigned short)count; } -bool learn_skill(unit * u, skill_t sk, double learn_chance) -{ - skill *sv = u->skills; - if (learn_chance < 1.0 && rng_int() % 10000 >= learn_chance * 10000) - if (!chance(learn_chance)) - return false; - while (sv != u->skills + u->skill_size) { - assert(sv->weeks > 0); - if (sv->id == sk) { - if (sv->weeks <= 1) { - sk_set(sv, sv->level + 1); - } - else { - sv->weeks--; - } - return true; - } - ++sv; - } - sv = add_skill(u, sk); - sk_set(sv, 1); - return true; -} - void remove_skill(unit * u, skill_t sk) { skill *sv = u->skills; @@ -1280,7 +1257,8 @@ static int att_modification(const unit * u, skill_t sk) bool allied = alliedunit(c->magician, u->faction, HELP_GUARD); if (allied) { if (effect > bonus) bonus = effect; - } else { + } + else { if (effect < malus) malus = effect; } } @@ -1329,7 +1307,7 @@ int eff_skill(const unit * u, const skill *sv, const region *r) { assert(u); if (!r) r = u->region; - if (sv && sv->level>0) { + if (sv && sv->level > 0) { int mlevel = sv->level + get_modifier(u, sv->id, sv->level, r, false); if (mlevel > 0) { @@ -1418,10 +1396,10 @@ void default_name(const unit *u, char name[], int len) { static const char * prefix[MAXLOCALES]; int i = locale_index(lang); /*if (!prefix[i]) {*/ - prefix[i] = LOC(lang, "unitdefault"); - if (!prefix[i]) { - prefix[i] = parameters[P_UNIT]; - } + prefix[i] = LOC(lang, "unitdefault"); + if (!prefix[i]) { + prefix[i] = parameters[P_UNIT]; + } /*}*/ result = prefix[i]; } @@ -1733,7 +1711,7 @@ void scale_number(unit * u, int n) } if (u->number > 0) { for (a = a_find(u->attribs, &at_effect); a && a->type == &at_effect; - a = a->next) { + a = a->next) { effect_data *data = (effect_data *)a->data.v; int snew = data->value / u->number * n; if (n) { @@ -1889,25 +1867,6 @@ bool unit_can_study(const unit *u) { return !((u_race(u)->flags & RCF_NOLEARN) || fval(u, UFL_WERE)); } -static double produceexp_chance(void) { - return config_get_flt("study.from_use", 1.0 / 3); -} - -void produceexp_ex(struct unit *u, skill_t sk, int n, bool (*learn)(unit *, skill_t, double)) -{ - if (n != 0 && (is_monsters(u->faction) || playerrace(u_race(u)))) { - double chance = produceexp_chance(); - if (chance > 0.0F) { - learn(u, sk, (n * chance) / u->number); - } - } -} - -void produceexp(struct unit *u, skill_t sk, int n) -{ - produceexp_ex(u, sk, n, learn_skill); -} - /* ID's für Einheiten und Zauber */ int newunitid(void) { @@ -1917,7 +1876,6 @@ int newunitid(void) start_random_no = random_unit_no; while (ufindhash(random_unit_no) || dfindhash(random_unit_no) - || cfindhash(random_unit_no) || forbiddenid(random_unit_no)) { random_unit_no++; if (random_unit_no == MAX_UNIT_NR + 1) { @@ -2022,11 +1980,6 @@ int maintenance_cost(const struct unit *u) { if (u == NULL) return MAINTENANCE; - if (global.functions.maintenance) { - int retval = global.functions.maintenance(u); - if (retval >= 0) - return retval; - } return u_race(u)->maintenance * u->number; } diff --git a/src/kernel/unit.h b/src/kernel/unit.h index e2dac5abe..2a74e4fbb 100644 --- a/src/kernel/unit.h +++ b/src/kernel/unit.h @@ -30,7 +30,7 @@ extern "C" { struct skill; struct item; struct sc_mage; - + struct gamedata; #define UFL_DEAD (1<<0) #define UFL_ISNEW (1<<1) /* 2 */ #define UFL_LONGACTION (1<<2) /* 4 */ @@ -162,9 +162,6 @@ extern "C" { struct skill *unit_skill(const struct unit *u, skill_t id); bool has_skill(const unit * u, skill_t sk); int effskill(const struct unit *u, skill_t sk, const struct region *r); - int SkillCap(skill_t sk); - void produceexp(struct unit *u, skill_t sk, int n); - void produceexp_ex(struct unit *u, skill_t sk, int n, bool (*learn)(unit *, skill_t, double)); void set_level(struct unit *u, skill_t id, int level); int get_level(const struct unit *u, skill_t id); @@ -186,7 +183,7 @@ extern "C" { /* see resolve.h */ int resolve_unit(variant data, void *address); void write_unit_reference(const struct unit *u, struct storage *store); - variant read_unit_reference(struct storage *store); + variant read_unit_reference(struct gamedata *data); bool leave(struct unit *u, bool force); bool can_leave(struct unit *u); @@ -209,8 +206,6 @@ extern "C" { void u_setfaction(struct unit *u, struct faction *f); void set_number(struct unit *u, int count); - bool learn_skill(struct unit *u, skill_t sk, double chance); - int invisible(const struct unit *target, const struct unit *viewer); void free_unit(struct unit *u); diff --git a/src/kernel/unit.test.c b/src/kernel/unit.test.c index 474d839ee..dc0484bf7 100644 --- a/src/kernel/unit.test.c +++ b/src/kernel/unit.test.c @@ -227,14 +227,13 @@ static void test_default_name(CuTest *tc) { test_cleanup(); test_create_world(); lang = get_or_create_locale("de"); - /* FIXME this has no real effect: default_name uses a static buffer that is initialized in some other test. This sucks. */ - locale_setstring(lang, "unitdefault", "Einheit"); + locale_setstring(lang, "unitdefault", "Zweiheit"); u = test_create_unit(test_create_faction(test_create_race("human")), findregion(0, 0)); default_name(u, buf, sizeof(buf)); - sprintf(compare, "Einheit %s", itoa36(u->no)); + sprintf(compare, "Zweiheit %s", itoa36(u->no)); CuAssertStrEquals(tc, compare, buf); test_cleanup(); @@ -341,32 +340,6 @@ static void test_age_familiar(CuTest *tc) { test_cleanup(); } -static CuTest *g_tc; - -static bool cb_learn_one(unit *u, skill_t sk, double chance) { - CuAssertIntEquals(g_tc, SK_ALCHEMY, sk); - CuAssertDblEquals(g_tc, 0.5 / u->number, chance, 0.01); - return false; -} - -static bool cb_learn_two(unit *u, skill_t sk, double chance) { - CuAssertIntEquals(g_tc, SK_ALCHEMY, sk); - CuAssertDblEquals(g_tc, 2 * 0.5 / u->number, chance, 0.01); - return false; -} - -static void test_produceexp(CuTest *tc) { - unit *u; - - g_tc = tc; - test_cleanup(); - u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); - config_set("study.from_use", "0.5"); - produceexp_ex(u, SK_ALCHEMY, 1, cb_learn_one); - produceexp_ex(u, SK_ALCHEMY, 2, cb_learn_two); - test_cleanup(); -} - static void test_inside_building(CuTest *tc) { unit *u; building *b; @@ -433,7 +406,6 @@ CuSuite *get_unit_suite(void) SUITE_ADD_TEST(suite, test_skill_familiar); SUITE_ADD_TEST(suite, test_age_familiar); SUITE_ADD_TEST(suite, test_inside_building); - SUITE_ADD_TEST(suite, test_produceexp); SUITE_ADD_TEST(suite, test_limited_skills); return suite; } diff --git a/src/kernel/version.h b/src/kernel/version.h index aa0de93ab..4aab16f5f 100644 --- a/src/kernel/version.h +++ b/src/kernel/version.h @@ -10,7 +10,7 @@ without prior permission by the authors of Eressea. */ -#define INTPAK_VERSION 329 /* in binary, ints can get packed */ +#define INTPAK_VERSION 329 /* in binary, ints can get packed. starting with E2/572 */ #define NOZEROIDS_VERSION 330 /* 2008-05-16 zero is not a valid ID for anything (including factions) */ #define NOBORDERATTRIBS_VERSION 331 /* 2008-05-17 connection::attribs has been moved to userdata */ #define UIDHASH_VERSION 332 /* 2008-05-22 borders use the region.uid to store */ @@ -35,10 +35,12 @@ #define ATTRIBOWNER_VERSION 351 /* all attrib_type functions know who owns the attribute */ #define BADCRYPT_VERSION 351 /* passwords are broken, 969.dat only. */ #define NOCRYPT_VERSION 352 /* passwords are plaintext again */ +#define ATHASH_VERSION 353 /* attribute-type hash, not name */ +#define NOWATCH_VERSION 354 /* plane->watchers is gone */ /* unfinished: */ #define CRYPT_VERSION 400 /* passwords are encrypted */ -#define RELEASE_VERSION NOCRYPT_VERSION /* current datafile */ +#define RELEASE_VERSION NOWATCH_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/kernel/xmlreader.c b/src/kernel/xmlreader.c index e1e476cd6..da8014d67 100644 --- a/src/kernel/xmlreader.c +++ b/src/kernel/xmlreader.c @@ -169,7 +169,7 @@ construction ** consPtr) con->skill = sk; con->maxsize = xml_ivalue(node, "maxsize", -1); con->minskill = xml_ivalue(node, "minskill", -1); - con->reqsize = xml_ivalue(node, "reqsize", -1); + con->reqsize = xml_ivalue(node, "reqsize", 1); con->defense_bonus = xml_ivalue(node, "defense_bonus", 0); con->close_combat_bonus = xml_ivalue(node, "close_combat_bonus", 0); con->ranged_bonus = xml_ivalue(node, "ranged_bonus", 0); @@ -898,9 +898,6 @@ static int parse_rules(xmlDocPtr doc) (int(*)(const struct region *, const struct faction *, const struct race *, int))fun; } - else if (strcmp((const char *)propValue, "maintenance") == 0) { - global.functions.maintenance = (int(*)(const struct unit *))fun; - } else { log_error("unknown function for rule '%s'\n", (const char *)propValue); } diff --git a/src/laws.c b/src/laws.c index f6cca67a6..3129bd16b 100755 --- a/src/laws.c +++ b/src/laws.c @@ -486,6 +486,7 @@ attrib_type at_germs = { DEFAULT_AGE, a_writeshorts, a_readshorts, + NULL, ATF_UNIQUE }; @@ -2217,26 +2218,13 @@ int send_cmd(unit * u, struct order *ord) return 0; } -static bool display_item(faction * f, unit * u, const item_type * itype) +static void display_item(unit * u, const item_type * itype) { + faction * f = u->faction; const char *name; const char *key; const char *info; - if (u != NULL) { - int i = i_get(u->items, itype); - if (i == 0) { - if (u->region->land != NULL) { - i = i_get(u->region->land->items, itype); - } - if (i == 0) { - i = i_get(u->faction->items, itype); - if (i == 0) - return false; - } - } - } - name = resourcename(itype->rtype, 0); key = mkname("iteminfo", name); info = locale_getstring(f->locale, key); @@ -2246,23 +2234,13 @@ static bool display_item(faction * f, unit * u, const item_type * itype) } ADDMSG(&f->msgs, msg_message("displayitem", "weight item description", itype->weight, itype->rtype, info)); - - return true; } -static bool display_potion(faction * f, unit * u, const potion_type * ptype) +static void display_potion(unit * u, const potion_type * ptype) { + faction * f = u->faction; attrib *a; - if (ptype == NULL) - return false; - else { - int i = i_get(u->items, ptype->itype); - if (i == 0 && 2 * ptype->level > effskill(u, SK_ALCHEMY, 0)) { - return false; - } - } - a = a_find(f->attribs, &at_showitem); while (a && a->data.v != ptype) a = a->next; @@ -2270,12 +2248,11 @@ static bool display_potion(faction * f, unit * u, const potion_type * ptype) a = a_add(&f->attribs, a_new(&at_showitem)); a->data.v = (void *)ptype->itype; } - - return true; } -static bool display_race(faction * f, unit * u, const race * rc) +static void display_race(unit * u, const race * rc) { + faction * f = u->faction; const char *name, *key; const char *info; int a, at_count; @@ -2283,8 +2260,6 @@ static bool display_race(faction * f, unit * u, const race * rc) size_t size = sizeof(buf) - 1; size_t bytes; - if (u && u_race(u) != rc) - return false; name = rc_name_s(rc, NAME_SINGULAR); bytes = slprintf(bufp, size, "%s: ", LOC(f->locale, name)); @@ -2298,7 +2273,7 @@ static bool display_race(faction * f, unit * u, const race * rc) info = LOC(f->locale, mkname("raceinfo", "no_info")); } - bufp = STRLCPY(bufp, info, size); + if (info) bufp = STRLCPY(bufp, info, size); /* hp_p : Trefferpunkte */ bytes = @@ -2416,17 +2391,72 @@ static bool display_race(faction * f, unit * u, const race * rc) *bufp = 0; addmessage(0, f, buf, MSG_EVENT, ML_IMPORTANT); +} - return true; +static void reshow_other(unit * u, struct order *ord, const char *s) { + int err = 21; + + if (s) { + const spell *sp = 0; + const item_type *itype; + const race *rc; + /* check if it's an item */ + itype = finditemtype(s, u->faction->locale); + sp = unit_getspell(u, s, u->faction->locale); + rc = findrace(s, u->faction->locale); + + if (itype) { + // if this is a potion, we need the right alchemy skill + int i = i_get(u->items, itype); + + err = 36; // we do not have this item? + if (i <= 0) { + // we don't have the item, but it may be a potion that we know + const potion_type *ptype = resource2potion(item2resource(itype)); + if (ptype) { + if (2 * ptype->level > effskill(u, SK_ALCHEMY, 0)) { + itype = NULL; + } + } else { + itype = NULL; + } + } + } + + if (itype) { + const potion_type *ptype = itype->rtype->ptype; + if (ptype) { + display_potion(u, ptype); + } + else { + display_item(u, itype); + } + return; + } + + if (sp) { + attrib *a = a_find(u->faction->attribs, &at_seenspell); + while (a != NULL && a->type == &at_seenspell && a->data.v != sp) { + a = a->next; + } + if (a != NULL) { + a_remove(&u->faction->attribs, a); + } + return; + } + + if (rc && u_race(u) == rc) { + display_race(u, rc); + return; + } + } + cmistake(u, ord, err, MSG_EVENT); } static void reshow(unit * u, struct order *ord, const char *s, param_t p) { int skill, c; const potion_type *ptype; - const item_type *itype; - const spell *sp = 0; - const race *rc; switch (p) { case P_ZAUBER: @@ -2437,48 +2467,15 @@ static void reshow(unit * u, struct order *ord, const char *s, param_t p) c = 0; for (ptype = potiontypes; ptype != NULL; ptype = ptype->next) { if (ptype->level * 2 <= skill) { - c += display_potion(u->faction, u, ptype); + display_potion(u, ptype); + ++c; } } if (c == 0) cmistake(u, ord, 285, MSG_EVENT); break; case NOPARAM: - if (s) { - /* check if it's an item */ - itype = finditemtype(s, u->faction->locale); - if (itype != NULL) { - ptype = resource2potion(item2resource(itype)); - if (ptype != NULL) { - if (display_potion(u->faction, u, ptype)) - break; - } - else { - if (!display_item(u->faction, u, itype)) - cmistake(u, ord, 36, MSG_EVENT); - - break; - } - } - /* try for a spell */ - sp = unit_getspell(u, s, u->faction->locale); - if (sp) { - attrib *a = a_find(u->faction->attribs, &at_seenspell); - while (a != NULL && a->type == &at_seenspell && a->data.v != sp) { - a = a->next; - } - if (a != NULL) { - a_remove(&u->faction->attribs, a); - } - break; - } - /* last, check if it's a race. */ - rc = findrace(s, u->faction->locale); - if (rc != NULL && display_race(u->faction, u, rc)) { - break; - } - } - cmistake(u, ord, 21, MSG_EVENT); + reshow_other(u, ord, s); break; default: cmistake(u, ord, 222, MSG_EVENT); @@ -2754,7 +2751,7 @@ void sinkships(struct region * r) static attrib_type at_number = { "faction_renum", - NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, ATF_UNIQUE }; @@ -4254,11 +4251,6 @@ bool rule_force_leave(int flags) { return (rules&flags) == flags; } -static void maintain_buildings_1(region * r) -{ - maintain_buildings(r, false); -} - void init_processor(void) { int p; @@ -4343,10 +4335,9 @@ void init_processor(void) p += 10; if (!keyword_disabled(K_PAY)) { - add_proc_order(p, K_PAY, pay_cmd, 0, "Gebaeudeunterhalt (disable)"); + add_proc_order(p, K_PAY, pay_cmd, 0, "Gebaeudeunterhalt (BEZAHLE NICHT)"); } - add_proc_postregion(p, maintain_buildings_1, - "Gebaeudeunterhalt (1. Versuch)"); + add_proc_postregion(p, maintain_buildings, "Gebaeudeunterhalt"); p += 10; /* QUIT fuer sich alleine */ add_proc_global(p, quit, "Sterben"); @@ -4447,8 +4438,7 @@ void update_subscriptions(void) FILE *F; char zText[MAX_PATH]; - strlcpy(zText, basepath(), sizeof(zText)); - strlcat(zText, "/subscriptions", sizeof(zText)); + join_path(basepath(), "subscriptions", zText, sizeof(zText)); F = fopen(zText, "r"); if (F == NULL) { log_warning(0, "could not open %s.\n", zText); @@ -4468,17 +4458,6 @@ void update_subscriptions(void) } } fclose(F); - - sprintf(zText, "subscriptions.%u", turn); - F = fopen(zText, "w"); - if (F) { - faction *f; - for (f = factions; f != NULL; f = f->next) { - fprintf(F, "%s:%u:%s:%s:%u:\n", - itoa36(f->no), f->subscription, f->email, dbrace(f->race), f->lastorders); - } - fclose(F); - } } bool diff --git a/src/laws.test.c b/src/laws.test.c index 64f65663c..32144fc4d 100644 --- a/src/laws.test.c +++ b/src/laws.test.c @@ -27,6 +27,7 @@ #include #include +#include static void test_new_building_can_be_renamed(CuTest * tc) { @@ -213,18 +214,16 @@ static void test_display_cmd(CuTest *tc) { test_cleanup(); r = test_create_region(0, 0, test_create_terrain("plain", LAND_REGION)); f = test_create_faction(0); - f->locale = get_or_create_locale("de"); assert(r && f); - test_translate_param(f->locale, P_UNIT, "EINHEIT"); u = test_create_unit(f, r); assert(u); - ord = create_order(K_DISPLAY, f->locale, "EINHEIT Hodor"); + ord = create_order(K_DISPLAY, f->locale, "%s Hodor", LOC(f->locale, parameters[P_UNIT])); CuAssertIntEquals(tc, 0, display_cmd(u, ord)); CuAssertStrEquals(tc, "Hodor", u->display); free_order(ord); - ord = create_order(K_DISPLAY, f->locale, "EINHEIT"); + ord = create_order(K_DISPLAY, f->locale, LOC(f->locale, parameters[P_UNIT])); CuAssertIntEquals(tc, 0, display_cmd(u, ord)); CuAssertPtrEquals(tc, NULL, u->display); free_order(ord); @@ -350,11 +349,6 @@ static void test_fishing_feeds_2_people(CuTest * tc) CuAssertIntEquals(tc, 32, i_get(u->items, rtype->itype)); } -static int not_so_hungry(const unit * u) -{ - return 6 * u->number; -} - static void test_fishing_does_not_give_goblins_money(CuTest * tc) { const resource_type *rtype; @@ -375,7 +369,6 @@ static void test_fishing_does_not_give_goblins_money(CuTest * tc) u_set_ship(u, sh); i_change(&u->items, rtype->itype, 42); - global.functions.maintenance = not_so_hungry; scale_number(u, 2); sh->flags |= SF_FISHING; get_food(r); @@ -583,7 +576,12 @@ static void test_new_units(CuTest *tc) { assert(loc); u->orders = create_order(K_MAKETEMP, loc, "hurr"); new_units(); - CuAssertPtrNotNull(tc, u->next); + CuAssertPtrNotNull(tc, u = u->next); + CuAssertIntEquals(tc, UFL_ISNEW, fval(u, UFL_ISNEW)); + CuAssertIntEquals(tc, 0, u->number); + CuAssertIntEquals(tc, 0, u->age); + CuAssertPtrEquals(tc, f, u->faction); + CuAssertStrEquals(tc, "EINHEIT hurr", u->_name); test_cleanup(); } @@ -779,34 +777,27 @@ static void test_luck_message(CuTest *tc) { static unit * setup_name_cmd(void) { faction *f; - struct locale *lang; test_cleanup(); f = test_create_faction(0); - f->locale = lang = get_or_create_locale("en"); - locale_setstring(lang, parameters[P_UNIT], "UNIT"); - locale_setstring(lang, parameters[P_REGION], "REGION"); - locale_setstring(lang, parameters[P_FACTION], "FACTION"); - locale_setstring(lang, parameters[P_BUILDING], "BUILDING"); - locale_setstring(lang, parameters[P_SHIP], "SHIP"); - init_parameters(lang); return test_create_unit(f, test_create_region(0, 0, 0)); } static void test_name_unit(CuTest *tc) { unit *u; + faction *f; order *ord; u = setup_name_cmd(); - - ord = create_order(K_NAME, u->faction->locale, "UNIT Hodor"); + f = u->faction; + ord = create_order(K_NAME, f->locale, "%s Hodor", LOC(f->locale, parameters[P_UNIT])); name_cmd(u, ord); CuAssertStrEquals(tc, "Hodor", u->_name); free_order(ord); - ord = create_order(K_NAME, u->faction->locale, "UNIT"); + ord = create_order(K_NAME, f->locale, LOC(f->locale, parameters[P_UNIT])); name_cmd(u, ord); - CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error84")); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error84")); CuAssertStrEquals(tc, "Hodor", u->_name); free_order(ord); @@ -815,22 +806,24 @@ static void test_name_unit(CuTest *tc) { static void test_name_region(CuTest *tc) { unit *u; + faction *f; order *ord; u = setup_name_cmd(); + f = u->faction; - ord = create_order(K_NAME, u->faction->locale, "REGION Hodor"); + ord = create_order(K_NAME, f->locale, "%s Hodor", LOC(f->locale, parameters[P_REGION])); name_cmd(u, ord); - CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error145")); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error145")); u->building = test_create_building(u->region, 0); name_cmd(u, ord); CuAssertStrEquals(tc, "Hodor", u->region->land->name); free_order(ord); - ord = create_order(K_NAME, u->faction->locale, "REGION"); + ord = create_order(K_NAME, f->locale, LOC(f->locale, parameters[P_REGION])); name_cmd(u, ord); - CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error84")); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error84")); CuAssertStrEquals(tc, "Hodor", u->region->land->name); free_order(ord); @@ -839,22 +832,24 @@ static void test_name_region(CuTest *tc) { static void test_name_building(CuTest *tc) { unit *u; + faction *f; order *ord; u = setup_name_cmd(); + f = u->faction; - ord = create_order(K_NAME, u->faction->locale, "BUILDING Hodor"); + ord = create_order(K_NAME, f->locale, "%s Hodor", LOC(f->locale, parameters[P_BUILDING])); name_cmd(u, ord); - CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error145")); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error145")); u->building = test_create_building(u->region, 0); name_cmd(u, ord); CuAssertStrEquals(tc, "Hodor", u->building->name); free_order(ord); - ord = create_order(K_NAME, u->faction->locale, "BUILDING"); + ord = create_order(K_NAME, f->locale, LOC(f->locale, parameters[P_BUILDING])); name_cmd(u, ord); - CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error84")); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error84")); CuAssertStrEquals(tc, "Hodor", u->building->name); free_order(ord); @@ -868,19 +863,21 @@ static void test_name_building(CuTest *tc) { static void test_name_ship(CuTest *tc) { unit *u; + faction *f; order *ord; u = setup_name_cmd(); + f = u->faction; u->ship = test_create_ship(u->region, 0); - ord = create_order(K_NAME, u->faction->locale, "SHIP Hodor"); + ord = create_order(K_NAME, f->locale, "%s Hodor", LOC(f->locale, parameters[P_SHIP])); name_cmd(u, ord); CuAssertStrEquals(tc, "Hodor", u->ship->name); free_order(ord); - ord = create_order(K_NAME, u->faction->locale, "SHIP"); + ord = create_order(K_NAME, f->locale, LOC(f->locale, parameters[P_SHIP])); name_cmd(u, ord); - CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error84")); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error84")); CuAssertStrEquals(tc, "Hodor", u->ship->name); free_order(ord); @@ -896,7 +893,6 @@ static void test_long_order_normal(CuTest *tc) { u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); fset(u, UFL_MOVED); fset(u, UFL_LONGACTION); - u->faction->locale = get_or_create_locale("de"); unit_addorder(u, ord = create_order(K_MOVE, u->faction->locale, 0)); update_long_order(u); CuAssertPtrEquals(tc, ord->data, u->thisorder->data); @@ -913,7 +909,6 @@ static void test_long_order_none(CuTest *tc) { unit *u; test_cleanup(); u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); - u->faction->locale = get_or_create_locale("de"); update_long_order(u); CuAssertPtrEquals(tc, 0, u->thisorder); CuAssertPtrEquals(tc, 0, u->orders); @@ -926,7 +921,6 @@ static void test_long_order_cast(CuTest *tc) { unit *u; test_cleanup(); u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); - u->faction->locale = get_or_create_locale("de"); unit_addorder(u, create_order(K_CAST, u->faction->locale, 0)); unit_addorder(u, create_order(K_CAST, u->faction->locale, 0)); update_long_order(u); @@ -941,7 +935,6 @@ static void test_long_order_buy_sell(CuTest *tc) { unit *u; test_cleanup(); u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); - u->faction->locale = get_or_create_locale("de"); unit_addorder(u, create_order(K_BUY, u->faction->locale, 0)); unit_addorder(u, create_order(K_SELL, u->faction->locale, 0)); unit_addorder(u, create_order(K_SELL, u->faction->locale, 0)); @@ -957,7 +950,6 @@ static void test_long_order_multi_long(CuTest *tc) { unit *u; test_cleanup(); u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); - u->faction->locale = get_or_create_locale("de"); unit_addorder(u, create_order(K_MOVE, u->faction->locale, 0)); unit_addorder(u, create_order(K_DESTROY, u->faction->locale, 0)); update_long_order(u); @@ -972,7 +964,6 @@ static void test_long_order_multi_buy(CuTest *tc) { unit *u; test_cleanup(); u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); - u->faction->locale = get_or_create_locale("de"); unit_addorder(u, create_order(K_BUY, u->faction->locale, 0)); unit_addorder(u, create_order(K_BUY, u->faction->locale, 0)); update_long_order(u); @@ -987,7 +978,6 @@ static void test_long_order_multi_sell(CuTest *tc) { unit *u; test_cleanup(); u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); - u->faction->locale = get_or_create_locale("de"); unit_addorder(u, create_order(K_SELL, u->faction->locale, 0)); unit_addorder(u, create_order(K_BUY, u->faction->locale, 0)); unit_addorder(u, create_order(K_SELL, u->faction->locale, 0)); @@ -1003,7 +993,6 @@ static void test_long_order_buy_cast(CuTest *tc) { unit *u; test_cleanup(); u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); - u->faction->locale = get_or_create_locale("de"); unit_addorder(u, create_order(K_BUY, u->faction->locale, 0)); unit_addorder(u, create_order(K_CAST, u->faction->locale, 0)); update_long_order(u); @@ -1014,22 +1003,18 @@ static void test_long_order_buy_cast(CuTest *tc) { } static void test_long_order_hungry(CuTest *tc) { - // FIXME: set_default_order is a test-only function, this is a bad test. - // see also default_order unit *u; test_cleanup(); config_set("hunger.long", "1"); u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); fset(u, UFL_HUNGER); - u->faction->locale = get_or_create_locale("de"); unit_addorder(u, create_order(K_MOVE, u->faction->locale, 0)); unit_addorder(u, create_order(K_DESTROY, u->faction->locale, 0)); - set_default_order(K_WORK); + config_set("orders.default", "work"); update_long_order(u); CuAssertIntEquals(tc, K_WORK, getkeyword(u->thisorder)); CuAssertPtrNotNull(tc, u->orders); CuAssertPtrEquals(tc, 0, u->faction->msgs); - set_default_order(NOKEYWORD); test_cleanup(); } @@ -1040,7 +1025,6 @@ static void test_ally_cmd_errors(CuTest *tc) { test_cleanup(); u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); - u->faction->locale = get_or_create_locale("de"); fid = u->faction->no + 1; CuAssertPtrEquals(tc, 0, findfaction(fid)); @@ -1056,36 +1040,30 @@ static void test_ally_cmd(CuTest *tc) { unit *u; faction * f; order *ord; - struct locale *lang; test_cleanup(); u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); f = test_create_faction(0); - u->faction->locale = lang = get_or_create_locale("de"); - locale_setstring(lang, parameters[P_NOT], "NICHT"); - locale_setstring(lang, parameters[P_GUARD], "BEWACHE"); - init_parameters(lang); - - ord = create_order(K_ALLY, lang, "%s", itoa36(f->no)); + ord = create_order(K_ALLY, f->locale, "%s", itoa36(f->no)); ally_cmd(u, ord); CuAssertPtrEquals(tc, 0, u->faction->msgs); CuAssertIntEquals(tc, HELP_ALL, alliedfaction(0, u->faction, f, HELP_ALL)); free_order(ord); - ord = create_order(K_ALLY, lang, "%s NICHT", itoa36(f->no)); + ord = create_order(K_ALLY, f->locale, "%s %s", itoa36(f->no), LOC(f->locale, parameters[P_NOT])); ally_cmd(u, ord); CuAssertPtrEquals(tc, 0, u->faction->msgs); CuAssertIntEquals(tc, 0, alliedfaction(0, u->faction, f, HELP_ALL)); free_order(ord); - ord = create_order(K_ALLY, lang, "%s BEWACHE", itoa36(f->no)); + ord = create_order(K_ALLY, f->locale, "%s %s", itoa36(f->no), LOC(f->locale, parameters[P_GUARD])); ally_cmd(u, ord); CuAssertPtrEquals(tc, 0, u->faction->msgs); CuAssertIntEquals(tc, HELP_GUARD, alliedfaction(0, u->faction, f, HELP_ALL)); free_order(ord); - ord = create_order(K_ALLY, lang, "%s BEWACHE NICHT", itoa36(f->no)); + ord = create_order(K_ALLY, f->locale, "%s %s %s", itoa36(f->no), LOC(f->locale, parameters[P_GUARD]), LOC(f->locale, parameters[P_NOT])); ally_cmd(u, ord); CuAssertPtrEquals(tc, 0, u->faction->msgs); CuAssertIntEquals(tc, 0, alliedfaction(0, u->faction, f, HELP_ALL)); @@ -1116,26 +1094,22 @@ static void test_nmr_warnings(CuTest *tc) { static unit * setup_mail_cmd(void) { faction *f; - struct locale *lang; test_cleanup(); f = test_create_faction(0); - f->locale = lang = get_or_create_locale("de"); - locale_setstring(lang, parameters[P_UNIT], "EINHEIT"); - locale_setstring(lang, parameters[P_REGION], "REGION"); - locale_setstring(lang, parameters[P_FACTION], "PARTEI"); - init_parameters(lang); return test_create_unit(f, test_create_region(0, 0, 0)); } static void test_mail_unit(CuTest *tc) { order *ord; unit *u; + faction *f; u = setup_mail_cmd(); - ord = create_order(K_MAIL, u->faction->locale, "EINHEIT %s 'Hodor!'", itoa36(u->no)); + f = u->faction; + ord = create_order(K_MAIL, f->locale, "%s %s 'Hodor!'", LOC(f->locale, parameters[P_UNIT]), itoa36(u->no)); mail_cmd(u, ord); - CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "unitmessage")); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "unitmessage")); free_order(ord); test_cleanup(); } @@ -1143,11 +1117,13 @@ static void test_mail_unit(CuTest *tc) { static void test_mail_faction(CuTest *tc) { order *ord; unit *u; + faction *f; u = setup_mail_cmd(); - ord = create_order(K_MAIL, u->faction->locale, "PARTEI %s 'Hodor!'", itoa36(u->faction->no)); + f = u->faction; + ord = create_order(K_MAIL, f->locale, "%s %s 'Hodor!'", LOC(f->locale, parameters[P_FACTION]), itoa36(u->faction->no)); mail_cmd(u, ord); - CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "regionmessage")); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "regionmessage")); free_order(ord); test_cleanup(); } @@ -1155,9 +1131,11 @@ static void test_mail_faction(CuTest *tc) { static void test_mail_region(CuTest *tc) { order *ord; unit *u; + faction *f; u = setup_mail_cmd(); - ord = create_order(K_MAIL, u->faction->locale, "REGION 'Hodor!'", itoa36(u->no)); + f = u->faction; + ord = create_order(K_MAIL, f->locale, "%s 'Hodor!'", LOC(f->locale, parameters[P_REGION]), itoa36(u->no)); mail_cmd(u, ord); CuAssertPtrNotNull(tc, test_find_messagetype(u->region->msgs, "mail_result")); free_order(ord); @@ -1166,52 +1144,60 @@ static void test_mail_region(CuTest *tc) { static void test_mail_unit_no_msg(CuTest *tc) { unit *u; + faction *f; order *ord; u = setup_mail_cmd(); - ord = create_order(K_MAIL, u->faction->locale, "EINHEIT %s", itoa36(u->no)); + f = u->faction; + ord = create_order(K_MAIL, f->locale, "%s %s", LOC(f->locale, parameters[P_UNIT]), itoa36(u->no)); mail_cmd(u, ord); - CuAssertPtrEquals(tc, 0, test_find_messagetype(u->faction->msgs, "unitmessage")); - CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error30")); + CuAssertPtrEquals(tc, 0, test_find_messagetype(f->msgs, "unitmessage")); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error30")); free_order(ord); test_cleanup(); } static void test_mail_faction_no_msg(CuTest *tc) { unit *u; + faction *f; order *ord; u = setup_mail_cmd(); - ord = create_order(K_MAIL, u->faction->locale, "PARTEI %s", itoa36(u->faction->no)); + f = u->faction; + ord = create_order(K_MAIL, f->locale, "%s %s", LOC(f->locale, parameters[P_FACTION]), itoa36(f->no)); mail_cmd(u, ord); - CuAssertPtrEquals(tc, 0, test_find_messagetype(u->faction->msgs, "regionmessage")); - CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error30")); + CuAssertPtrEquals(tc, 0, test_find_messagetype(f->msgs, "regionmessage")); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error30")); free_order(ord); test_cleanup(); } static void test_mail_faction_no_target(CuTest *tc) { unit *u; + faction *f; order *ord; u = setup_mail_cmd(); - ord = create_order(K_MAIL, u->faction->locale, "PARTEI %s", itoa36(u->faction->no+1)); + f = u->faction; + ord = create_order(K_MAIL, f->locale, "%s %s", LOC(f->locale, parameters[P_FACTION]), itoa36(f->no+1)); mail_cmd(u, ord); - CuAssertPtrEquals(tc, 0, test_find_messagetype(u->faction->msgs, "regionmessage")); - CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error66")); + CuAssertPtrEquals(tc, 0, test_find_messagetype(f->msgs, "regionmessage")); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error66")); free_order(ord); test_cleanup(); } static void test_mail_region_no_msg(CuTest *tc) { unit *u; + faction *f; order *ord; u = setup_mail_cmd(); - ord = create_order(K_MAIL, u->faction->locale, "REGION"); + f = u->faction; + ord = create_order(K_MAIL, f->locale, LOC(f->locale, parameters[P_REGION])); mail_cmd(u, ord); CuAssertPtrEquals(tc, 0, test_find_messagetype(u->region->msgs, "mail_result")); - CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error30")); + CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "error30")); free_order(ord); test_cleanup(); } @@ -1236,32 +1222,109 @@ static void test_show_without_item(CuTest *tc) f = test_create_faction(test_create_race("human")); u = test_create_unit(f, r); - ord = create_order(K_RESHOW, u->faction->locale, "testname"); + ord = create_order(K_RESHOW, f->locale, "testname"); itype = it_get_or_create(rt_get_or_create("testitem")); i = i_new(itype, 1); reshow_cmd(u, ord); - CuAssertTrue(tc, test_find_messagetype(u->faction->msgs, "error21") != NULL); - test_clear_messages(u->faction); + CuAssertTrue(tc, test_find_messagetype(f->msgs, "error21") != NULL); + test_clear_messages(f); locale_setstring(loc, "testitem", "testname"); locale_setstring(loc, "iteminfo::testitem", "testdescription"); reshow_cmd(u, ord); - CuAssertTrue(tc, test_find_messagetype(u->faction->msgs, "error21") == NULL); - CuAssertTrue(tc, test_find_messagetype(u->faction->msgs, "error36") != NULL); - test_clear_messages(u->faction); + CuAssertTrue(tc, test_find_messagetype(f->msgs, "error21") == NULL); + CuAssertTrue(tc, test_find_messagetype(f->msgs, "error36") != NULL); + test_clear_messages(f); i_add(&(u->items), i); reshow_cmd(u, ord); - CuAssertTrue(tc, test_find_messagetype(u->faction->msgs, "error21") == NULL); - CuAssertTrue(tc, test_find_messagetype(u->faction->msgs, "error36") == NULL); - test_clear_messages(u->faction); + CuAssertTrue(tc, test_find_messagetype(f->msgs, "error21") == NULL); + CuAssertTrue(tc, test_find_messagetype(f->msgs, "error36") == NULL); + test_clear_messages(f); free_order(ord); test_cleanup(); } +static void test_show_elf(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)); + 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, "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; + unit *u; + struct locale *loc; + message * msg; + + test_cleanup(); + + mt_register(mt_new_va("msg_event", "string:string", 0)); + test_create_race("human"); + rc = test_create_race("elf"); + + loc = get_or_create_locale("de"); + locale_setstring(loc, "race::elf_p", "Elfen"); + locale_setstring(loc, "race::elf", "Elf"); + locale_setstring(loc, "race::human_p", "Menschen"); + locale_setstring(loc, "race::human", "Mensch"); + init_locale(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, "Mensch"); + reshow_cmd(u, ord); + CuAssertTrue(tc, test_find_messagetype(u->faction->msgs, "error21") != NULL); + CuAssertTrue(tc, test_find_messagetype(u->faction->msgs, "msg_event") == NULL); + test_clear_messages(u->faction); + free_order(ord); + + ord = create_order(K_RESHOW, loc, "Elf"); + reshow_cmd(u, ord); + CuAssertTrue(tc, test_find_messagetype(u->faction->msgs, "error21") == 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 int low_wage(const region * r, const faction * f, const race * rc, int in_turn) { return 1; } @@ -1397,6 +1460,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_immigration); SUITE_ADD_TEST(suite, test_demon_hunger); diff --git a/src/lighthouse.c b/src/lighthouse.c index 88193ecc7..6192db142 100644 --- a/src/lighthouse.c +++ b/src/lighthouse.c @@ -66,7 +66,7 @@ void update_lighthouse(building * lh) int lighthouse_range(const building * b, const faction * f) { int d = 0; - if (fval(b, BLD_WORKING) && b->size >= 10) { + if (fval(b, BLD_MAINTAINED) && b->size >= 10) { int maxd = (int)log10(b->size) + 1; if (skill_enabled(SK_PERCEPTION)) { @@ -112,7 +112,7 @@ bool check_leuchtturm(region * r, faction * f) building *b = (building *)a->data.v; assert(b->type == bt_find("lighthouse")); - if (fval(b, BLD_WORKING) && b->size >= 10) { + if (fval(b, BLD_MAINTAINED) && b->size >= 10) { int maxd = (int)log10(b->size) + 1; if (skill_enabled(SK_PERCEPTION) && f) { diff --git a/src/magic.c b/src/magic.c index 488c9abe3..d24612e06 100644 --- a/src/magic.c +++ b/src/magic.c @@ -22,6 +22,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "magic.h" #include "skill.h" +#include "study.h" #include "laws.h" #include @@ -54,7 +55,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* util includes */ #include -#include +#include #include #include #include @@ -67,6 +68,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include +#include #include /* libc includes */ @@ -128,16 +130,17 @@ typedef struct icastle_data { int time; } icastle_data; -static int a_readicastle(attrib * a, void *owner, struct storage *store) +static int a_readicastle(attrib * a, void *owner, struct gamedata *data) { - icastle_data *data = (icastle_data *)a->data.v; + storage *store = data->store; + icastle_data *idata = (icastle_data *)a->data.v; char token[32]; READ_TOK(store, token, sizeof(token)); - if (global.data_version < ATTRIBOWNER_VERSION) { + if (data->version < ATTRIBOWNER_VERSION) { READ_INT(store, NULL); } - READ_INT(store, &data->time); - data->type = bt_find(token); + READ_INT(store, &idata->time); + idata->type = bt_find(token); return AT_READ_OK; } @@ -254,8 +257,9 @@ int get_spell_level_mage(const spell * sp, void * cbdata) return sbe ? sbe->level : 0; } -static int read_mage(attrib * a, void *owner, struct storage *store) +static int read_mage(attrib * a, void *owner, struct gamedata *data) { + storage *store = data->store; int i, mtype; sc_mage *mage = (sc_mage *)a->data.v; char spname[64]; @@ -291,10 +295,10 @@ static int read_mage(attrib * a, void *owner, struct storage *store) } } if (mage->magietyp == M_GRAY) { - read_spellbook(&mage->spellbook, store, get_spell_level_mage, mage); + read_spellbook(&mage->spellbook, data, get_spell_level_mage, mage); } else { - read_spellbook(0, store, 0, mage); + read_spellbook(0, data, 0, mage); } return AT_READ_OK; } @@ -335,6 +339,7 @@ attrib_type at_mage = { NULL, write_mage, read_mage, + NULL, ATF_UNIQUE }; @@ -361,8 +366,9 @@ sc_mage *get_mage(const unit * u) * Spruch zu seiner List-of-known-spells hinzugefügt werden. */ -static int read_seenspell(attrib * a, void *owner, struct storage *store) +static int read_seenspell(attrib * a, void *owner, struct gamedata *data) { + storage *store = data->store; int i; spell *sp = 0; char token[32]; @@ -373,7 +379,7 @@ static int read_seenspell(attrib * a, void *owner, struct storage *store) sp = find_spellbyid((unsigned int)i); } else { - if (global.data_version < UNIQUE_SPELLS_VERSION) { + if (data->version < UNIQUE_SPELLS_VERSION) { READ_INT(store, 0); /* ignore mtype */ } sp = find_spell(token); @@ -2280,10 +2286,10 @@ static int resolve_familiar(variant data, void *addr) return result; } -static int read_familiar(attrib * a, void *owner, struct storage *store) +static int read_familiar(attrib * a, void *owner, struct gamedata *data) { int result = - read_reference(&a->data.v, store, read_unit_reference, resolve_familiar); + read_reference(&a->data.v, data, read_unit_reference, resolve_familiar); if (result == 0 && a->data.v == NULL) { return AT_READ_FAIL; } @@ -2364,10 +2370,10 @@ static int resolve_clone(variant data, void *addr) return result; } -static int read_clone(attrib * a, void *owner, struct storage *store) +static int read_clone(attrib * a, void *owner, struct gamedata *data) { int result = - read_reference(&a->data.v, store, read_unit_reference, resolve_clone); + read_reference(&a->data.v, data, read_unit_reference, resolve_clone); if (result == 0 && a->data.v == NULL) { return AT_READ_FAIL; } @@ -2391,10 +2397,10 @@ static int resolve_mage(variant data, void *addr) return result; } -static int read_magician(attrib * a, void *owner, struct storage *store) +static int read_magician(attrib * a, void *owner, struct gamedata *data) { int result = - read_reference(&a->data.v, store, read_unit_reference, resolve_mage); + read_reference(&a->data.v, data, read_unit_reference, resolve_mage); if (result == 0 && a->data.v == NULL) { return AT_READ_FAIL; } @@ -2416,6 +2422,7 @@ attrib_type at_familiarmage = { age_unit, a_write_unit, read_magician, + NULL, ATF_UNIQUE }; @@ -2426,6 +2433,7 @@ attrib_type at_familiar = { age_unit, a_write_unit, read_familiar, + NULL, ATF_UNIQUE }; @@ -2436,6 +2444,7 @@ attrib_type at_clonemage = { age_unit, a_write_unit, read_magician, + NULL, ATF_UNIQUE }; @@ -2446,6 +2455,7 @@ attrib_type at_clone = { age_unit, a_write_unit, read_clone, + NULL, ATF_UNIQUE }; diff --git a/src/magic.test.c b/src/magic.test.c index ad698625a..ff6cb1e29 100644 --- a/src/magic.test.c +++ b/src/magic.test.c @@ -387,12 +387,11 @@ void test_multi_cast(CuTest *tc) { sp->cast = cast_fireball; CuAssertPtrEquals(tc, sp, find_spell("fireball")); - u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); - u->faction->locale = lang = get_or_create_locale("de"); - locale_setstring(lang, parameters[P_ANY], "ALLE"); - init_parameters(lang); + lang = test_create_locale(); locale_setstring(lang, mkname("spell", sp->sname), "Feuerball"); CuAssertStrEquals(tc, "Feuerball", spell_name(sp, lang)); + + u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); set_level(u, SK_MAGIC, 10); unit_add_spell(u, 0, sp, 1); CuAssertPtrEquals(tc, sp, unit_getspell(u, "Feuerball", lang)); diff --git a/src/main.c b/src/main.c index 305d4a901..d011a473b 100644 --- a/src/main.c +++ b/src/main.c @@ -96,6 +96,7 @@ static void parse_config(const char *filename) #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 } } @@ -128,9 +129,33 @@ static int get_arg(int argc, char **argv, size_t len, int index, const char **re return index; } +static int verbosity_to_flags(int verbosity) { + int flags = 0; + switch (verbosity) { + case 0: + flags = 0; + break; + case 1: + flags = LOG_CPERROR; + break; + case 2: + flags = LOG_CPERROR | LOG_CPWARNING; + break; + case 3: + flags = LOG_CPERROR | LOG_CPWARNING | LOG_CPINFO; + break; + default: + flags = LOG_CPERROR | LOG_CPWARNING | LOG_CPINFO | LOG_CPDEBUG; + break; + } + return flags; +} + static int parse_args(int argc, char **argv, int *exitcode) { - int i, log_stderr = 0; + int i; + int log_stderr = LOG_CPERROR; + int log_flags = LOG_CPERROR | LOG_CPWARNING | LOG_CPINFO; for (i = 1; i != argc; ++i) { char *argi = argv[i]; @@ -168,7 +193,8 @@ static int parse_args(int argc, char **argv, int *exitcode) i = get_arg(argc, argv, 2, i, &luafile, 0); break; case 'l': - i = get_arg(argc, argv, 2, i, &logfile, 0); + i = get_arg(argc, argv, 2, i, &arg, 0); + log_flags = arg ? atoi(arg) : 0xff; break; case 't': i = get_arg(argc, argv, 2, i, &arg, 0); @@ -192,28 +218,15 @@ static int parse_args(int argc, char **argv, int *exitcode) } } - switch (verbosity) { - case 0: - log_stderr = 0; - break; - case 1: - log_stderr = LOG_CPERROR; - break; - case 2: - log_stderr = LOG_CPERROR | LOG_CPWARNING; - break; - case 3: - log_stderr = LOG_CPERROR | LOG_CPWARNING | LOG_CPINFO; - break; - default: - log_stderr = LOG_CPERROR | LOG_CPWARNING | LOG_CPINFO | LOG_CPDEBUG; - break; - } + // open logfile on disk: + log_flags = verbosity_to_flags(log_flags); + log_open(logfile, log_flags); + // also log to stderr: + log_stderr = verbosity_to_flags(verbosity); if (log_stderr) { log_to_file(log_stderr | LOG_FLUSH | LOG_BRIEF, stderr); } - return 0; } @@ -296,7 +309,6 @@ int main(int argc, char **argv) /* parse arguments again, to override ini file */ parse_args(argc, argv, &err); - log_open(logfile); locale_init(); #ifdef CRTDBG diff --git a/src/market.c b/src/market.c index 3d96c30e4..efcaf0d98 100644 --- a/src/market.c +++ b/src/market.c @@ -63,7 +63,7 @@ static void free_market(attrib * a) attrib_type at_market = { "script", NULL, free_market, NULL, - NULL, NULL, ATF_UNIQUE + NULL, NULL, NULL, ATF_UNIQUE }; static int rc_luxury_trade(const struct race *rc) diff --git a/src/market.test.c b/src/market.test.c index 6132db39b..43e070b5e 100644 --- a/src/market.test.c +++ b/src/market.test.c @@ -67,7 +67,7 @@ static void test_market_curse(CuTest * tc) } r = findregion(1, 1); b = test_create_building(r, btype); - b->flags |= BLD_WORKING; + b->flags |= BLD_MAINTAINED; b->size = b->type->maxsize; f = test_create_faction(0); diff --git a/src/modules/arena.c b/src/modules/arena.c index 6b76a3434..97488218a 100644 --- a/src/modules/arena.c +++ b/src/modules/arena.c @@ -48,6 +48,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include #include #include @@ -248,10 +249,10 @@ write_hurting(const attrib * a, const void *owner, struct storage *store) WRITE_INT(store, b->no); } -static int read_hurting(attrib * a, void *owner, struct storage *store) +static int read_hurting(attrib * a, void *owner, struct gamedata *data) { int i; - READ_INT(store, &i); + READ_INT(data->store, &i); a->data.v = (void *)findbuilding(i); if (a->data.v == NULL) { log_error("temple of pain is broken\n"); @@ -448,10 +449,10 @@ static void caldera_write(const trigger * t, struct storage *store) write_building_reference(b, store); } -static int caldera_read(trigger * t, struct storage *store) +static int caldera_read(trigger * t, struct gamedata *data) { int rb = - read_reference(&t->data.v, store, read_building_reference, + read_reference(&t->data.v, data, read_building_reference, resolve_building); if (rb == 0 && !t->data.v) { return AT_READ_FAIL; diff --git a/src/modules/gmcmd.c b/src/modules/gmcmd.c index 02bd2b889..b9e1bc342 100644 --- a/src/modules/gmcmd.c +++ b/src/modules/gmcmd.c @@ -29,12 +29,15 @@ #include #include #include +#include #include #include #include /* util includes */ #include +#include + #include /* libc includes */ @@ -43,18 +46,18 @@ #include #include -static int read_permissions(attrib * a, void *owner, struct storage *store) +static int read_permissions(attrib * a, void *owner, struct gamedata *data) { assert(!a); - a_read(store, &a, owner); + read_attribs(data, &a, owner); a_remove(&a, a); return AT_READ_OK; } -static int read_gmcreate(attrib * a, void *owner, struct storage *store) +static int read_gmcreate(attrib * a, void *owner, struct gamedata *data) { char zText[32]; - READ_TOK(store, zText, sizeof(zText)); + READ_TOK(data->store, zText, sizeof(zText)); return AT_READ_OK; } diff --git a/src/modules/museum.c b/src/modules/museum.c index e1e7d33e5..5ad9092a4 100644 --- a/src/modules/museum.c +++ b/src/modules/museum.c @@ -43,6 +43,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include #include @@ -79,11 +80,11 @@ struct storage *store) } static int -a_readmuseumgivebackcookie(attrib * a, void *owner, struct storage *store) +a_readmuseumgivebackcookie(attrib * a, void *owner, gamedata *data) { museumgivebackcookie *gbc = (museumgivebackcookie *)a->data.v; - READ_INT(store, &gbc->warden_no); - READ_INT(store, &gbc->cookie); + READ_INT(data->store, &gbc->warden_no); + READ_INT(data->store, &gbc->cookie); return AT_READ_OK; } @@ -121,11 +122,11 @@ struct storage *store) write_items(store, gb->items); } -static int a_readmuseumgiveback(attrib * a, void *owner, struct storage *store) +static int a_readmuseumgiveback(attrib * a, void *owner, struct gamedata *data) { museumgiveback *gb = (museumgiveback *)a->data.v; - READ_INT(store, &gb->cookie); - read_items(store, &gb->items); + READ_INT(data->store, &gb->cookie); + read_items(data->store, &gb->items); return AT_READ_OK; } @@ -194,7 +195,7 @@ void warden_add_give(unit * src, unit * u, const item_type * itype, int n) void create_museum(void) { -#if 0 /* TODO: move this to LUA. It should be possible. */ +#if 0 /* TODO: move this to Lua. It should be possible. */ unsigned int museum_id = hashstring("museum"); plane *museum = getplanebyid(museum_id); region *r; diff --git a/src/modules/score.c b/src/modules/score.c index ed8f4e458..2fd3cf230 100644 --- a/src/modules/score.c +++ b/src/modules/score.c @@ -152,7 +152,7 @@ void score(void) allscores = 1; } - sprintf(path, "%s/score", basepath()); + join_path(basepath(), "score", path, sizeof(path)); scoreFP = fopen(path, "w"); if (scoreFP) { const unsigned char utf8_bom[4] = { 0xef, 0xbb, 0xbf, 0 }; @@ -167,7 +167,7 @@ void score(void) fprintf(scoreFP, "(%s) ", score); fprintf(scoreFP, "%30.30s (%3.3s) %5s (%3d)\n", f->name, - rc_name_s(f->race, NAME_SINGULAR), + f->race->_name, factionid(f), f->age); } @@ -177,7 +177,7 @@ void score(void) alliance *a; const item_type *token = it_find("conquesttoken"); - sprintf(path, "%s/score.alliances", basepath()); + join_path(basepath(), "score.alliances", path, sizeof(path)); scoreFP = fopen(path, "w"); if (scoreFP) { const unsigned char utf8_bom[4] = { 0xef, 0xbb, 0xbf, 0 }; diff --git a/src/modules/xmas.c b/src/modules/xmas.c index 1aa8aaba7..4b90af68e 100644 --- a/src/modules/xmas.c +++ b/src/modules/xmas.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -47,10 +48,10 @@ static void xmasgate_write(const trigger * t, struct storage *store) WRITE_TOK(store, itoa36(b->no)); } -static int xmasgate_read(trigger * t, struct storage *store) +static int xmasgate_read(trigger * t, struct gamedata *data) { int bc = - read_reference(&t->data.v, store, read_building_reference, + read_reference(&t->data.v, data, read_building_reference, resolve_building); if (bc == 0 && !t->data.v) { return AT_READ_FAIL; diff --git a/src/monster.c b/src/monster.c index 03faa1830..5eafba1ea 100644 --- a/src/monster.c +++ b/src/monster.c @@ -25,9 +25,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "give.h" #include "move.h" -/* triggers includes */ -#include - /* attributes includes */ #include #include diff --git a/src/monsters.c b/src/monsters.c index 634c6db54..2fc2fcde3 100644 --- a/src/monsters.c +++ b/src/monsters.c @@ -26,9 +26,7 @@ #include "monster.h" #include "laws.h" #include "keyword.h" - -/* triggers includes */ -#include +#include "study.h" /* attributes includes */ #include @@ -549,21 +547,23 @@ static order *monster_seeks_target(region * r, unit * u) } #endif -static const char *random_growl(void) +void random_growl(const unit *u, region *target, int rand) { - switch (rng_int() % 5) { - case 0: - return "Groammm"; - case 1: - return "Roaaarrrr"; - case 2: - return "Chhhhhhhhhh"; - case 3: - return "Tschrrrkk"; - case 4: - return "Schhhh"; + const struct locale *lang = u->faction->locale; + const char *growl; + switch(rand){ + case 1: growl = "growl1"; break; + case 2: growl = "growl2"; break; + case 3: growl = "growl3"; break; + case 4: growl = "growl4"; break; + default: growl = "growl0"; + } + + + if (rname(target, lang)) { + message *msg = msg_message("dragon_growl", "dragon number target growl", u, u->number, target, growl); + ADDMSG(&u->region->msgs, msg); } - return ""; } extern struct attrib_type at_direction; @@ -709,17 +709,7 @@ static order *plan_dragon(unit * u) reduce_weight(u); } if (rng_int() % 100 < 15) { - const struct locale *lang = u->faction->locale; - /* do a growl */ - if (rname(tr, lang)) { - addlist(&u->orders, - create_order(K_MAIL, lang, "%s '%s... %s %s %s'", - LOC(lang, parameters[P_REGION]), - random_growl(), - u->number == - 1 ? "Ich rieche" : "Wir riechen", - "etwas in", rname(tr, u->faction->locale))); - } + random_growl(u, tr, rng_int() % 5); } } else { diff --git a/src/monsters.test.c b/src/monsters.test.c index 9fce059f2..dd69e6bdb 100644 --- a/src/monsters.test.c +++ b/src/monsters.test.c @@ -12,9 +12,13 @@ #include "monster.h" #include "guard.h" +#include "reports.h" #include "skill.h" +#include "study.h" #include +#include +#include #include #include @@ -25,56 +29,26 @@ extern void plan_monsters(struct faction *f); extern int monster_attacks(unit * monster, bool respect_buildings, bool rich_only); -static void init_language(void) -{ - struct locale* lang; - int i; - - lang = get_or_create_locale("de"); - locale_setstring(lang, "skill::unarmed", "Waffenloser Kampf"); - locale_setstring(lang, "keyword::attack", "ATTACKIERE"); - locale_setstring(lang, "keyword::study", "LERNE"); - locale_setstring(lang, "keyword::tax", "TREIBE"); - locale_setstring(lang, "keyword::loot", "PLUENDERE"); - locale_setstring(lang, "keyword::piracy", "PIRATERIE"); - locale_setstring(lang, "keyword::guard", "BEWACHE"); - locale_setstring(lang, "keyword::move", "NACH"); - locale_setstring(lang, "keyword::message", "BOTSCHAFT"); - locale_setstring(lang, "REGION", "REGION"); - locale_setstring(lang, "east", "O"); - - for (i = 0; i < MAXKEYWORDS; ++i) { - if (!locale_getstring(lang, mkname("keyword", keywords[i]))) - locale_setstring(lang, mkname("keyword", keywords[i]), keywords[i]); - } - for (i = 0; i < MAXSKILLS; ++i) { - if (!locale_getstring(lang, mkname("skill", skillnames[i]))) - locale_setstring(lang, mkname("skill", skillnames[i]), skillnames[i]); - } - init_keywords(lang); - init_skills(lang); -} - static order *find_order(const char *expected, const unit *unit) { char cmd[32]; - order *order; - for (order = unit->orders; order; order = order->next) { - if (strcmp(expected, get_command(order, cmd, sizeof(cmd))) == 0) { - return order; + order *ord; + for (ord = unit->orders; ord; ord = ord->next) { + if (strcmp(expected, get_command(ord, cmd, sizeof(cmd))) == 0) { + return ord; } } return NULL; } -static void create_monsters(faction **player, faction **monsters, region **r, unit **u, unit **m) { +static void create_monsters(faction **player, faction **monsters, unit **u, unit **m) { race* rc; + region *r; test_cleanup(); - init_language(); - - test_create_world(); + test_create_horse(); + default_locale = test_create_locale(); *player = test_create_faction(NULL); *monsters = get_or_create_monsters(); assert(rc_find((*monsters)->race->_name)); @@ -83,22 +57,22 @@ static void create_monsters(faction **player, faction **monsters, region **r, un fset(rc, RCF_NPC); fset(*monsters, FFL_NOIDLEOUT); assert(fval(*monsters, FFL_NPC) && fval((*monsters)->race, RCF_UNARMEDGUARD) && fval((*monsters)->race, RCF_NPC) && fval(*monsters, FFL_NOIDLEOUT)); - (*monsters)->locale = default_locale; - *r = findregion(0, 0); + test_create_region(-1, 0, test_create_terrain("ocean", SEA_REGION | SWIM_INTO | FLY_INTO)); + test_create_region(1, 0, 0); + r = test_create_region(0, 0, 0); - *u = test_create_unit(*player, *r); + *u = test_create_unit(*player, r); unit_setid(*u, 1); - *m = test_create_unit(*monsters, *r); + *m = test_create_unit(*monsters, r); } static void test_monsters_attack(CuTest * tc) { faction *f, *f2; - region *r; unit *u, *m; - create_monsters(&f, &f2, &r, &u, &m); + create_monsters(&f, &f2, &u, &m); guard(m, GUARD_TAX); @@ -106,7 +80,7 @@ static void test_monsters_attack(CuTest * tc) plan_monsters(f2); - CuAssertPtrNotNull(tc, find_order("ATTACKIERE 1", m)); + CuAssertPtrNotNull(tc, find_order("attack 1", m)); test_cleanup(); } @@ -116,8 +90,8 @@ static void test_monsters_attack_ocean(CuTest * tc) region *r; unit *u, *m; - create_monsters(&f, &f2, &r, &u, &m); - r = findregion(-1, 0); + create_monsters(&f, &f2, &u, &m); + r = findregion(-1, 0); // ocean u = test_create_unit(u->faction, r); unit_setid(u, 2); m = test_create_unit(m->faction, r); @@ -127,21 +101,20 @@ static void test_monsters_attack_ocean(CuTest * tc) plan_monsters(f2); - CuAssertPtrNotNull(tc, find_order("ATTACKIERE 2", m)); + CuAssertPtrNotNull(tc, find_order("attack 2", m)); test_cleanup(); } static void test_monsters_waiting(CuTest * tc) { faction *f, *f2; - region *r; unit *u, *m; - create_monsters(&f, &f2, &r, &u, &m); + create_monsters(&f, &f2, &u, &m); guard(m, GUARD_TAX); fset(m, UFL_ISNEW); monster_attacks(m, false, false); - CuAssertPtrEquals(tc, 0, find_order("ATTACKIERE 1", m)); + CuAssertPtrEquals(tc, 0, find_order("attack 1", m)); test_cleanup(); } @@ -152,8 +125,8 @@ static void test_seaserpent_piracy(CuTest * tc) unit *u, *m; race *rc; - create_monsters(&f, &f2, &r, &u, &m); - r = findregion(-1, 0); + create_monsters(&f, &f2, &u, &m); + r = findregion(-1, 0); // ocean u = test_create_unit(u->faction, r); unit_setid(u, 2); m = test_create_unit(m->faction, r); @@ -165,18 +138,17 @@ static void test_seaserpent_piracy(CuTest * tc) config_set("rules.monsters.attack_chance", "1"); plan_monsters(f2); - CuAssertPtrNotNull(tc, find_order("PIRATERIE", m)); - CuAssertPtrNotNull(tc, find_order("ATTACKIERE 2", m)); + CuAssertPtrNotNull(tc, find_order("piracy", m)); + CuAssertPtrNotNull(tc, find_order("attack 2", m)); test_cleanup(); } static void test_monsters_attack_not(CuTest * tc) { faction *f, *f2; - region *r; unit *u, *m; - create_monsters(&f, &f2, &r, &u, &m); + create_monsters(&f, &f2, &u, &m); guard(m, GUARD_TAX); guard(u, GUARD_TAX); @@ -185,24 +157,23 @@ static void test_monsters_attack_not(CuTest * tc) plan_monsters(f2); - CuAssertPtrEquals(tc, 0, find_order("ATTACKIERE 1", m)); + CuAssertPtrEquals(tc, 0, find_order("attack 1", m)); test_cleanup(); } static void test_dragon_attacks_the_rich(CuTest * tc) { faction *f, *f2; - region *r; unit *u, *m; const item_type *i_silver; - init_language(); - create_monsters(&f, &f2, &r, &u, &m); + create_monsters(&f, &f2, &u, &m); + init_resources(); guard(m, GUARD_TAX); set_level(m, SK_WEAPONLESS, 10); - rsetmoney(r, 1); + rsetmoney(findregion(0, 0), 1); rsetmoney(findregion(1, 0), 0); i_silver = it_find("money"); assert(i_silver); @@ -212,39 +183,54 @@ static void test_dragon_attacks_the_rich(CuTest * tc) plan_monsters(f2); - CuAssertPtrNotNull(tc, find_order("ATTACKIERE 1", m)); - CuAssertPtrNotNull(tc, find_order("PLUENDERE", m)); + CuAssertPtrNotNull(tc, find_order("attack 1", m)); + CuAssertPtrNotNull(tc, find_order("loot", m)); test_cleanup(); } +extern void random_growl(const unit *u, region *tr, int rand); + static void test_dragon_moves(CuTest * tc) { faction *f, *f2; region *r; unit *u, *m; + struct message *msg; - create_monsters(&f, &f2, &r, &u, &m); + create_monsters(&f, &f2, &u, &m); + rsetmoney(findregion(1, 0), 1000); + r = findregion(0, 0); // plain rsetpeasants(r, 0); rsetmoney(r, 0); - rsetmoney(findregion(1, 0), 1000); set_level(m, SK_WEAPONLESS, 10); config_set("rules.monsters.attack_chance", ".0"); plan_monsters(f2); - CuAssertPtrNotNull(tc, find_order("NACH O", m)); + CuAssertPtrNotNull(tc, find_order("move east", m)); + + mt_register(mt_new_va("dragon_growl", "dragon:unit", "number:int", "target:region", "growl:string", 0)); + + random_growl(m, findregion(1, 0), 3); + + msg = test_get_last_message(r->msgs); + assert_message(tc, msg, "dragon_growl", 4); + assert_pointer_parameter(tc, msg, 0, m); + assert_int_parameter(tc, msg, 1, 1); + assert_pointer_parameter(tc, msg, 2, findregion(1,0)); + assert_string_parameter(tc, msg, 3, "growl3"); + test_cleanup(); } static void test_monsters_learn_exp(CuTest * tc) { faction *f, *f2; - region *r; unit *u, *m; skill* sk; - create_monsters(&f, &f2, &r, &u, &m); - config_set("study.from_use", "1"); + create_monsters(&f, &f2, &u, &m); + config_set("study.produceexp", "30"); u_setrace(u, u_race(m)); produceexp(u, SK_MELEE, u->number); diff --git a/src/move.c b/src/move.c index 19b0b86af..09f3c2b3f 100644 --- a/src/move.c +++ b/src/move.c @@ -22,6 +22,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "move.h" #include "laws.h" #include "reports.h" +#include "study.h" #include "alchemy.h" #include "travelthru.h" #include "vortex.h" @@ -59,6 +60,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include #include #include @@ -122,7 +124,7 @@ get_followers(unit * target, region * r, const region_list * route_end, unit *uf; for (uf = r->units; uf; uf = uf->next) { if (fval(uf, UFL_FOLLOWING) && !fval(uf, UFL_NOTMOVING)) { - const attrib *a = a_findc(uf->attribs, &at_follow); + const attrib *a = a_find(uf->attribs, &at_follow); if (a && a->data.v == target) { follower *fnew = (follower *)malloc(sizeof(follower)); fnew->uf = uf; @@ -154,8 +156,9 @@ static int shiptrail_age(attrib * a, void *owner) return (t->age > 0) ? AT_AGE_KEEP : AT_AGE_REMOVE; } -static int shiptrail_read(attrib * a, void *owner, struct storage *store) +static int shiptrail_read(attrib * a, void *owner, struct gamedata *data) { + storage *store = data->store; int n; traveldir *t = (traveldir *)(a->data.v); @@ -674,12 +677,6 @@ int check_ship_allowed(struct ship *sh, const region * r) } if (is_freezing(u)) { - unit *captain = ship_owner(sh); - if (captain) { - ADDMSG(&captain->faction->msgs, - msg_message("detectforbidden", "unit region", u, r)); - } - return SA_NO_INSECT; } } @@ -780,9 +777,26 @@ static void msg_to_ship_inmates(ship *sh, unit **firstu, unit **lastu, message * msg_release(msg); } +region * drift_target(ship *sh) { + int d, d_offset = rng_int() % MAXDIRECTIONS; + region *rnext = NULL; + for (d = 0; d != MAXDIRECTIONS; ++d) { + region *rn; + direction_t dir = (direction_t)((d + d_offset) % MAXDIRECTIONS); + rn = rconnect(sh->region, dir); + if (rn != NULL && check_ship_allowed(sh, rn) >= 0) { + rnext = rn; + if (!fval(rnext->terrain, SEA_REGION)) { + // prefer drifting towards non-ocean regions + break; + } + } + } + return rnext; +} + static void drifting_ships(region * r) { - direction_t d; bool drift = config_get_int("rules.ship.drifting", 1) != 0; double damage_drift = config_get_flt("rules.ship.damage_drift", 0.02); @@ -793,7 +807,6 @@ static void drifting_ships(region * r) region *rnext = NULL; region_list *route = NULL; unit *firstu = r->units, *lastu = NULL, *captain; - int d_offset; direction_t dir = 0; double ovl; @@ -828,17 +841,7 @@ static void drifting_ships(region * r) } else { /* Auswahl einer Richtung: Zuerst auf Land, dann * zufällig. Falls unmögliches Resultat: vergiß es. */ - d_offset = rng_int () % MAXDIRECTIONS; - for (d = 0; d != MAXDIRECTIONS; ++d) { - region *rn; - dir = (direction_t)((d + d_offset) % MAXDIRECTIONS); - rn = rconnect(r, dir); - if (rn != NULL && fval(rn->terrain, SAIL_INTO) && check_ship_allowed(sh, rn) > 0) { - rnext = rn; - if (!fval(rnext->terrain, SEA_REGION)) - break; - } - } + rnext = drift_target(sh); } if (rnext != NULL) { @@ -1755,7 +1758,7 @@ unit *owner_buildingtyp(const region * r, const building_type * bt) for (b = rbuildings(r); b; b = b->next) { owner = building_owner(b); if (b->type == bt && owner != NULL) { - if (b->size >= bt->maxsize) { + if (building_finished(b)) { return owner; } } @@ -1783,8 +1786,7 @@ bool can_takeoff(const ship * sh, const region * from, const region * to) return true; } -static void -sail(unit * u, order * ord, bool move_on_land, region_list ** routep) +static void sail(unit * u, order * ord, region_list ** routep) { region *starting_point = u->region; region *current_point, *last_point; @@ -1906,12 +1908,10 @@ sail(unit * u, order * ord, bool move_on_land, region_list ** routep) } // storms_enabled if (!fval(tthis, SEA_REGION)) { if (!fval(tnext, SEA_REGION)) { - if (!move_on_land) { - /* check that you're not traveling from one land region to another. */ - ADDMSG(&u->faction->msgs, msg_message("shipnoshore", - "ship region", sh, next_point)); - break; - } + /* check that you're not traveling from one land region to another. */ + ADDMSG(&u->faction->msgs, msg_message("shipnoshore", + "ship region", sh, next_point)); + break; } else { if (!can_takeoff(sh, current_point, next_point)) { @@ -1943,7 +1943,10 @@ sail(unit * u, order * ord, bool move_on_land, region_list ** routep) reason = check_ship_allowed(sh, next_point); if (reason < 0) { /* for some reason or another, we aren't allowed in there.. */ - if (check_leuchtturm(current_point, NULL) || reason == SA_NO_INSECT) { + if (reason == SA_NO_INSECT) { + ADDMSG(&f->msgs, msg_message("detectforbidden", "unit region", u, sh->region)); + } + else if (check_leuchtturm(current_point, NULL)) { ADDMSG(&f->msgs, msg_message("sailnolandingstorm", "ship region", sh, next_point)); } else { @@ -2018,9 +2021,7 @@ sail(unit * u, order * ord, bool move_on_land, region_list ** routep) * transferiert wurden, kann der aktuelle Befehl gelöscht werden. */ cycle_route(ord, u, step); set_order(&u->thisorder, NULL); - if (!move_on_land) { - set_coast(sh, last_point, current_point); - } + set_coast(sh, last_point, current_point); if (is_cursed(sh->attribs, C_SHIP_FLYING, 0)) { ADDMSG(&f->msgs, msg_message("shipfly", "ship from to", sh, @@ -2266,13 +2267,13 @@ static void travel(unit * u, region_list ** routep) } } -void move_cmd(unit * u, order * ord, bool move_on_land) +void move_cmd(unit * u, order * ord) { region_list *route = NULL; assert(u->number); if (u->ship && u == ship_owner(u->ship)) { - sail(u, ord, move_on_land, &route); + sail(u, ord, &route); } else { travel(u, &route); @@ -2390,7 +2391,7 @@ int follow_ship(unit * u, order * ord) init_tokens_str(command); getstrtoken(); /* NACH ausführen */ - move_cmd(u, ord, false); + move_cmd(u, ord); return 1; /* true -> Einheitenliste von vorne durchgehen */ } @@ -2557,13 +2558,13 @@ void movement(void) if (ships) { if (u->ship && ship_owner(u->ship) == u) { init_order(u->thisorder); - move_cmd(u, u->thisorder, false); + move_cmd(u, u->thisorder); } } else { if (!u->ship || ship_owner(u->ship) != u) { init_order(u->thisorder); - move_cmd(u, u->thisorder, false); + move_cmd(u, u->thisorder); } } } diff --git a/src/move.h b/src/move.h index 0417dcec5..70dddbb0b 100644 --- a/src/move.h +++ b/src/move.h @@ -74,15 +74,16 @@ extern "C" { bool move_blocked(const struct unit *u, const struct region *src, const struct region *dest); bool can_takeoff(const struct ship * sh, const struct region * from, const struct region * to); - void move_cmd(struct unit * u, struct order * ord, bool move_on_land); + void move_cmd(struct unit * u, struct order * ord); int follow_ship(struct unit * u, struct order * ord); -#define SA_HARBOUR 2 -#define SA_COAST 1 +#define SA_HARBOUR 1 +#define SA_COAST 0 #define SA_NO_INSECT -1 #define SA_NO_COAST -2 int check_ship_allowed(struct ship *sh, const struct region * r); + struct region * drift_target(struct ship *sh); #ifdef __cplusplus } #endif diff --git a/src/move.test.c b/src/move.test.c index c4e9944c9..2ce9cccda 100644 --- a/src/move.test.c +++ b/src/move.test.c @@ -35,11 +35,10 @@ static void test_ship_not_allowed_in_coast(CuTest * tc) ship_type *stype; test_cleanup(); - test_create_world(); - - ttype = test_create_terrain("glacier", LAND_REGION | ARCTIC_REGION | WALK_INTO | SAIL_INTO); - otype = test_create_terrain("ocean", SEA_REGION | SAIL_INTO); + ttype = test_create_terrain("glacier", LAND_REGION | ARCTIC_REGION | WALK_INTO); + otype = test_create_terrain("ocean", SEA_REGION); stype = test_create_shiptype("derp"); + free(stype->coasts); stype->coasts = (struct terrain_type **)calloc(2, sizeof(struct terrain_type *)); r1 = test_create_region(0, 0, ttype); @@ -50,6 +49,7 @@ static void test_ship_not_allowed_in_coast(CuTest * tc) CuAssertIntEquals(tc, SA_NO_COAST, check_ship_allowed(sh, r1)); stype->coasts[0] = ttype; CuAssertIntEquals(tc, SA_COAST, check_ship_allowed(sh, r1)); + test_cleanup(); } typedef struct move_fixture { @@ -68,16 +68,15 @@ static void setup_harbor(move_fixture *mf) { unit *u; test_cleanup(); - test_create_world(); - ttype = test_create_terrain("glacier", LAND_REGION | ARCTIC_REGION | WALK_INTO | SAIL_INTO); + ttype = test_create_terrain("glacier", LAND_REGION | ARCTIC_REGION | WALK_INTO); btype = test_create_buildingtype("harbour"); sh = test_create_ship(0, 0); r = test_create_region(0, 0, ttype); b = test_create_building(r, btype); - b->flags |= BLD_WORKING; + b->flags |= BLD_MAINTAINED; u = test_create_unit(test_create_faction(0), r); u->ship = sh; @@ -233,7 +232,7 @@ static void test_ship_trails(CuTest *tc) { region_list *route = 0; test_cleanup(); - otype = test_create_terrain("ocean", SEA_REGION | SAIL_INTO); + otype = test_create_terrain("ocean", SEA_REGION); r1 = test_create_region(0, 0, otype); r2 = test_create_region(1, 0, otype); r3 = test_create_region(2, 0, otype); @@ -285,7 +284,6 @@ struct drift_fixture { unit *u; terrain_type *t_ocean; ship_type *st_boat; - struct locale *lang; ship *sh; }; @@ -293,7 +291,6 @@ struct drift_fixture { void setup_drift (struct drift_fixture *fix) { test_cleanup(); config_set("rules.ship.storms", "0"); - fix->lang = get_or_create_locale("de"); test_create_world(); test_create_shiptype("drifter"); @@ -301,12 +298,10 @@ void setup_drift (struct drift_fixture *fix) { fix->st_boat->cabins = 20000; fix->u = test_create_unit(fix->f = test_create_faction(0), fix->r=findregion(-1,0)); - assert(fix->r && fix->r->terrain->flags & SAIL_INTO); + assert(fix->r); set_level(fix->u, SK_SAILING, fix->st_boat->sumskill); u_set_ship(fix->u, fix->sh = test_create_ship(fix->u->region, fix->st_boat)); assert(fix->f && fix->u && fix->sh); - fix->f->locale = get_or_create_locale("de"); - } static void test_ship_no_overload(CuTest *tc) { @@ -461,7 +456,6 @@ static void test_follow_ship_msg(CuTest * tc) { const ship_type *stype; message *msg; order *ord; - traveldir *td = NULL; attrib *a; @@ -489,10 +483,6 @@ static void test_follow_ship_msg(CuTest * tc) { td->dir = D_NORTHWEST; td->age = 2; - locale_setstring(default_locale, "northwest", "Nordwesten"); - locale_setstring(default_locale, "keyword::move", "NACH"); - init_locale(default_locale); - mt_register(mt_new_va("error18", "unit:unit", "region:region", "command:order", 0)); init_order(ord); @@ -508,6 +498,24 @@ static void test_follow_ship_msg(CuTest * tc) { test_cleanup(); } +static void test_drifting_ships(CuTest *tc) { + ship *sh; + region *r1, *r2, *r3; + terrain_type *t_ocean, *t_plain; + ship_type *st_boat; + test_cleanup(); + t_ocean = test_create_terrain("ocean", SEA_REGION); + t_plain = test_create_terrain("plain", LAND_REGION); + r1 = test_create_region(0, 0, t_ocean); + r2 = test_create_region(1, 0, t_ocean); + st_boat = test_create_shiptype("boat"); + sh = test_create_ship(r1, st_boat); + CuAssertPtrEquals(tc, r2, drift_target(sh)); + r3 = test_create_region(-1, 0, t_plain); + CuAssertPtrEquals(tc, r3, drift_target(sh)); + test_cleanup(); +} + CuSuite *get_move_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -531,5 +539,6 @@ CuSuite *get_move_suite(void) SUITE_ADD_TEST(suite, test_ship_ridiculous_overload_no_captain); SUITE_ADD_TEST(suite, test_ship_damage_overload); SUITE_ADD_TEST(suite, test_follow_ship_msg); + SUITE_ADD_TEST(suite, test_drifting_ships); return suite; } diff --git a/src/piracy.c b/src/piracy.c index e8864faa9..1a556f92f 100644 --- a/src/piracy.c +++ b/src/piracy.c @@ -151,7 +151,7 @@ void piracy_cmd(unit * u, order *ord) // TODO this could still result in an illegal movement order (through a wall or whatever) // which will be prevented by move_cmd below if (rc && - ((sh && fval(rc->terrain, SAIL_INTO) && can_takeoff(sh, r, rc)) + ((sh && !fval(rc->terrain, FORBIDDEN_REGION) && can_takeoff(sh, r, rc)) || (canswim(u) && fval(rc->terrain, SWIM_INTO) && fval(rc->terrain, SEA_REGION)))) { for (sh2 = rc->ships; sh2; sh2 = sh2->next) { @@ -206,7 +206,7 @@ void piracy_cmd(unit * u, order *ord) /* Bewegung ausführen */ init_order(u->thisorder); - move_cmd(u, ord, true); + move_cmd(u, ord); } void age_piracy(region *r) { diff --git a/src/piracy.test.c b/src/piracy.test.c index dee326980..1c266fd47 100644 --- a/src/piracy.test.c +++ b/src/piracy.test.c @@ -26,7 +26,7 @@ static void setup_piracy(void) { lang = get_or_create_locale("de"); locale_setstring(lang, directions[D_EAST], "OSTEN"); init_directions(lang); - test_create_terrain("ocean", SAIL_INTO | SEA_REGION); + test_create_terrain("ocean", SEA_REGION); st_boat = test_create_shiptype("boat"); st_boat->cargo = 1000; } @@ -47,6 +47,7 @@ static void setup_pirate(unit **pirate, int p_r_flags, int p_rc_flags, const cha if (v_shiptype) { st_boat = st_get_or_create(v_shiptype); u_set_ship(*victim, test_create_ship((*victim)->region, st_boat)); + free(st_boat->coasts); st_boat->coasts = (struct terrain_type **)calloc(2, sizeof(struct terrain_type *)); st_boat->coasts[0] = vterrain; st_boat->coasts[1] = 0; @@ -59,6 +60,7 @@ static void setup_pirate(unit **pirate, int p_r_flags, int p_rc_flags, const cha if (p_shiptype) { st_boat = st_get_or_create(p_shiptype); u_set_ship(*pirate, test_create_ship((*pirate)->region, st_boat)); + free(st_boat->coasts); st_boat->coasts = (struct terrain_type **)calloc(2, sizeof(struct terrain_type *)); st_boat->coasts[0] = vterrain; st_boat->coasts[1] = 0; @@ -176,22 +178,39 @@ static void test_piracy_cmd_walking(CuTest * tc) { } static void test_piracy_cmd_land_to_land(CuTest * tc) { - unit *pirate, *victim; + unit *u; order *ord; region *r; + faction *f; + int target; + const terrain_type *t_plain; + const ship_type *stype; test_cleanup(); - setup_pirate(&pirate, 0, 0, "boat", &ord, &victim, SAIL_INTO, "boat"); - set_level(pirate, SK_SAILING, pirate->ship->type->sumskill); - r = pirate->region; + setup_piracy(); + t_plain = get_or_create_terrain("plain"); + stype = test_create_shiptype("boat"); - piracy_cmd(pirate, ord); - CuAssertPtrEquals(tc, 0, pirate->thisorder); - CuAssertTrue(tc, pirate->region == r); - /* TODO check message - CuAssertPtrNotNullMsg(tc, "successful PIRACY movement", test_find_messagetype(pirate->faction->msgs, "travel")); - */ + // create a target: + r = test_create_region(0, 0, t_plain); + f = test_create_faction(0); + u = test_create_unit(f, r); + u->ship = test_create_ship(r, stype); + target = f->no; + + // create a pirate: + r = test_create_region(1, 0, t_plain); + f = test_create_faction(0); + u = test_create_unit(f, r); + u->ship = test_create_ship(r, stype); + set_level(u, SK_SAILING, u->ship->type->sumskill); + ord = create_order(K_PIRACY, f->locale, "%s", itoa36(target)); + + piracy_cmd(u, ord); + CuAssertPtrEquals(tc, 0, u->thisorder); + CuAssertPtrEquals(tc, r, u->region); + // TODO check message free_order(ord); test_cleanup(); @@ -224,7 +243,7 @@ CuSuite *get_piracy_suite(void) SUITE_ADD_TEST(suite, test_piracy_cmd_errors); SUITE_ADD_TEST(suite, test_piracy_cmd); SUITE_ADD_TEST(suite, test_piracy_cmd_walking); - DISABLE_TEST(suite, test_piracy_cmd_land_to_land); + SUITE_ADD_TEST(suite, test_piracy_cmd_land_to_land); SUITE_ADD_TEST(suite, test_piracy_cmd_swimmer); return suite; } diff --git a/src/randenc.c b/src/randenc.c index ae533f9ea..6a642557b 100644 --- a/src/randenc.c +++ b/src/randenc.c @@ -25,6 +25,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "move.h" #include "alchemy.h" #include "chaos.h" +#include "study.h" /* kernel includes */ #include @@ -150,22 +151,17 @@ static void dissolve_units(void) remove_empty_units(); } -static int improve_all(faction * f, skill_t sk, int by_weeks) +static bool improve_all(faction * f, skill_t sk, int by_weeks) { unit *u; - bool ret = by_weeks; - + bool result = false; for (u = f->units; u; u = u->nextF) { if (has_skill(u, sk)) { - int weeks = 0; - for (; weeks != by_weeks; ++weeks) { - learn_skill(u, sk, 1.0); - ret = 0; - } + learn_skill(u, sk, by_weeks * STUDYDAYS); + result = true; } } - - return ret; + return result; } void find_manual(region * r, unit * u) @@ -246,10 +242,8 @@ void find_manual(region * r, unit * u) msg_release(msg); } - if (improve_all(u->faction, skill, 3) == 3) { - int i; - for (i = 0; i != 9; ++i) - learn_skill(u, skill, 1.0); + if (!improve_all(u->faction, skill, 3)) { + learn_skill(u, skill, 9 * STUDYDAYS); } } @@ -957,38 +951,7 @@ static void demon_skillchanges(void) unit *u; for (u = r->units; u; u = u->next) { if (u_race(u) == get_race(RC_DAEMON)) { - skill *sv = u->skills; - int upchance = 15; - int downchance = 10; - - if (fval(u, UFL_HUNGER)) { - /* hungry demons only go down, never up in skill */ - int rule_hunger = config_get_int("hunger.demons.skill", 0) != 0; - if (rule_hunger) { - upchance = 0; - downchance = 15; - } - } - - while (sv != u->skills + u->skill_size) { - int roll = rng_int() % 100; - if (sv->level > 0 && roll < upchance + downchance) { - int weeks = 1 + rng_int() % 3; - if (roll < downchance) { - reduce_skill(u, sv, weeks); - if (sv->level < 1) { - /* demons should never forget below 1 */ - set_level(u, sv->id, 1); - } - } - else { - while (weeks--) { - learn_skill(u, sv->id, 1.0); - } - } - } - ++sv; - } + demon_skillchange(u); } } } diff --git a/src/report.c b/src/report.c index 323a94f02..6436fe990 100644 --- a/src/report.c +++ b/src/report.c @@ -915,7 +915,7 @@ static void describe(stream *out, const seen_region * sr, faction * f) for (b = get_borders(r, r2); b;) { struct edge *e = edges; bool transparent = b->type->transparent(b, f); - const char *name = b->type->name(b, r, f, GF_DETAILED | GF_ARTICLE); + const char *name = border_name(b, r, f, GF_DETAILED | GF_ARTICLE); if (!transparent) see[d] = false; @@ -1376,15 +1376,7 @@ static int buildingmaintenance(const building * b, const resource_type * rtype) { const building_type *bt = b->type; int c, cost = 0; - static bool init = false; - static const curse_type *nocost_ct; - if (!init) { - init = true; - nocost_ct = ct_find("nocostbuilding"); - } - if (curse_active(get_curse(b->attribs, nocost_ct))) { - return 0; - } + for (c = 0; bt->maintenance && bt->maintenance[c].number; ++c) { const maintenance *m = bt->maintenance + c; if (m->rtype == rtype) { @@ -1403,13 +1395,14 @@ report_template(const char *filename, report_context * ctx, const char *charset) const resource_type *rsilver = get_resourcetype(R_SILVER); faction *f = ctx->f; region *r; - FILE *F = fopen(filename, "wt"); + FILE *F = fopen(filename, "w"); stream strm = { 0 }, *out = &strm; seen_region *sr = NULL; char buf[8192], *bufp; size_t size; int bytes; bool utf8 = _strcmpl(charset, "utf8") == 0 || _strcmpl(charset, "utf-8") == 0; + const curse_type *nocost_ct = ct_find("nocostbuilding"); if (F == NULL) { perror(filename); @@ -1484,15 +1477,16 @@ report_template(const char *filename, report_context * ctx, const char *charset) WARN_STATIC_BUFFER(); if (u->building && building_owner(u->building) == u) { building *b = u->building; - int cost = buildingmaintenance(b, rsilver); - - if (cost > 0) { - bytes = (int)strlcpy(bufp, ",U", size); - if (wrptr(&bufp, &size, bytes) != 0) - WARN_STATIC_BUFFER(); - bytes = (int)strlcpy(bufp, itoa10(cost), size); - if (wrptr(&bufp, &size, bytes) != 0) - WARN_STATIC_BUFFER(); + if (!curse_active(get_curse(b->attribs, nocost_ct))) { + int cost = buildingmaintenance(b, rsilver); + if (cost > 0) { + bytes = (int)strlcpy(bufp, ",U", size); + if (wrptr(&bufp, &size, bytes) != 0) + WARN_STATIC_BUFFER(); + bytes = (int)strlcpy(bufp, itoa10(cost), size); + if (wrptr(&bufp, &size, bytes) != 0) + WARN_STATIC_BUFFER(); + } } } else if (u->ship) { @@ -1898,7 +1892,7 @@ const faction * f) } } - if (b->size < b->type->maxsize) { + if (!building_finished(b)) { bytes = (int)strlcpy(bufp, LOC(lang, "nr_building_inprogress"), size); if (wrptr(&bufp, &size, bytes) != 0) WARN_STATIC_BUFFER(); @@ -2067,7 +2061,7 @@ const char *charset) unsigned char op; int maxh, bytes, ix = want(O_STATISTICS); int wants_stats = (f->options & ix); - FILE *F = fopen(filename, "wt"); + FILE *F = fopen(filename, "w"); stream strm = { 0 }, *out = &strm; seen_region *sr = NULL; char buf[8192]; diff --git a/src/reports.c b/src/reports.c index 595f92ae3..8dfd055c4 100644 --- a/src/reports.c +++ b/src/reports.c @@ -354,7 +354,7 @@ const char **illusion) bt_illusion = bt_find("illusioncastle"); if (bt_illusion && b->type == bt_illusion) { - const attrib *a = a_findc(b->attribs, &at_icastle); + const attrib *a = a_find(b->attribs, &at_icastle); if (a != NULL) { *illusion = buildingtype(icastle_type(a), b, b->size); } @@ -1540,22 +1540,24 @@ int write_reports(faction * f, time_t ltime) } prepare_report(&ctx, f); get_addresses(&ctx); - log_debug("Reports for %s:", factionname(f)); + log_debug("Reports for %s", factionname(f)); for (rtype = report_types; rtype != NULL; rtype = rtype->next) { if (f->options & rtype->flag) { - int error; + int error = 0; do { - char filename[MAX_PATH]; - sprintf(filename, "%s/%d-%s.%s", reportpath(), turn, factionid(f), + char filename[32]; + char path[MAX_PATH]; + sprintf(filename, "%d-%s.%s", turn, factionid(f), rtype->extension); - error = 0; - if (rtype->write(filename, &ctx, encoding) == 0) { + join_path(reportpath(), filename, path, sizeof(path)); + errno = 0; + if (rtype->write(path, &ctx, encoding) == 0) { gotit = true; } if (errno) { char zText[64]; - log_warning("retrying, error %d during %s report for faction %s", error, rtype->extension, factionname(f)); - sprintf(zText, "waiting %u seconds before we retry", backup / 1000); + log_warning("retrying, error %d during %s report for faction %s", errno, rtype->extension, factionname(f)); + sprintf(zText, "waiting %u seconds before we retry", backup); perror(zText); _sleep(backup); if (backup < maxbackup) { @@ -1644,15 +1646,6 @@ int reports(void) if (mailit) fclose(mailit); free_seen(); -#ifdef GLOBAL_REPORT - { - const char *str = config_get("globalreport"); - if (str != NULL) { - sprintf(path, "%s/%s.%u.cr", reportpath(), str, turn); - global_report(path); - } - } -#endif return retval; } diff --git a/src/reports.test.c b/src/reports.test.c index dd2c544eb..ebfe794cb 100644 --- a/src/reports.test.c +++ b/src/reports.test.c @@ -253,7 +253,6 @@ static void test_write_unit(CuTest *tc) { race *rc; struct locale *lang; char buffer[1024]; - /* FIXME: test emits ERROR: no translation for combat status status_aggressive in locale de */ test_cleanup(); rc = rc_get_or_create("human"); @@ -317,6 +316,12 @@ static void setup_spell_fixture(spell_fixture * spf) { spf->sbe = spellbook_get(spf->spb, spf->sp); } +static void cleanup_spell_fixture(spell_fixture *spf) { + spellbook_clear(spf->spb); + free(spf->spb); + test_cleanup(); +} + static void check_spell_syntax(CuTest *tc, char *msg, spell_fixture *spell, char *syntax) { stream strm; char buf[1024]; @@ -406,7 +411,7 @@ static void test_write_spell_syntax(CuTest *tc) { check_spell_syntax(tc, "kc+", &spell, " ZAUBERE \"Testzauber\" ( REGION | EINHEIT [ ...] | SCHIFF \n [ ...] | BURG [ ...] )"); - test_cleanup(); + cleanup_spell_fixture(&spell); } CuSuite *get_reports_suite(void) diff --git a/src/spells.c b/src/spells.c index 6724f39a5..1da440df2 100644 --- a/src/spells.c +++ b/src/spells.c @@ -30,7 +30,6 @@ #include #include #include -#include #include /* kernel includes */ @@ -62,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -975,7 +975,7 @@ static int sp_blessstonecircle(castorder * co) return 0; } - if (b->size < b->type->maxsize) { + if (!building_finished(b)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "error_notcomplete", "building", b)); return 0; @@ -2866,9 +2866,10 @@ static curse *mk_deathcloud(unit * mage, region * r, double force, int duration) #define COMPAT_DEATHCLOUD #ifdef COMPAT_DEATHCLOUD -static int dc_read_compat(struct attrib *a, void *target, struct storage * store) +static int dc_read_compat(struct attrib *a, void *target, gamedata *data) /* return AT_READ_OK on success, AT_READ_FAIL if attrib needs removal */ { + struct storage *store = data->store; region *r = NULL; unit *u; variant var; @@ -3091,9 +3092,9 @@ static int sp_summonshadowlords(castorder * co) static bool chaosgate_valid(const connection * b) { - const attrib *a = a_findc(b->from->attribs, &at_direction); + const attrib *a = a_find(b->from->attribs, &at_direction); if (!a) - a = a_findc(b->to->attribs, &at_direction); + a = a_find(b->to->attribs, &at_direction); if (!a) return false; return true; @@ -4501,11 +4502,11 @@ int sp_icastle(castorder * co) if (type == bt_illusion) { b->size = (rng_int() % (int)((power * power) + 1) * 10); } - else if (type->maxsize == -1) { - b->size = ((rng_int() % (int)(power)) + 1) * 5; + else if (type->maxsize >0) { + b->size = type->maxsize; } else { - b->size = type->maxsize; + b->size = ((rng_int() % (int)(power)) + 1) * 5; } if (type->name == NULL) { @@ -6508,7 +6509,6 @@ static spelldata spell_functions[] = { { "bad_dreams", sp_baddreams, 0 }, { "mindblast", sp_mindblast_temp, 0 }, { "orkdream", sp_sweetdreams, 0 }, - { "summon_alp", sp_summon_alp, 0 }, // TODO: this spell is disabled everywhere /* M_CERDDOR */ { "appeasement", sp_denyattack, 0 }, { "song_of_healing", sp_healing, 0 }, @@ -6705,9 +6705,6 @@ void register_spells(void) at_register(&at_wdwpyramid); at_register(&at_deathcloud_compat); - /* sp_summon_alp */ - register_alp(); - /* init_firewall(); */ ct_register(&ct_firewall); ct_register(&ct_deathcloud); diff --git a/src/spells/CMakeLists.txt b/src/spells/CMakeLists.txt index 6219708d4..fcc80b2cb 100644 --- a/src/spells/CMakeLists.txt +++ b/src/spells/CMakeLists.txt @@ -1,6 +1,5 @@ PROJECT(spells C) SET(_FILES -alp.c borders.c buildingcurse.c combatspells.c diff --git a/src/spells/alp.c b/src/spells/alp.c deleted file mode 100644 index d443aa063..000000000 --- a/src/spells/alp.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * - * Eressea PB(E)M host Copyright (C) 1998-2015 - * Christian Schlittchen (corwin@amber.kn-bremen.de) - * Katja Zedel (katze@felidae.kn-bremen.de) - * Henning Peters (faroul@beyond.kn-bremen.de) - * Enno Rehling (enno@eressea.de) - * Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de) - * - * This program may not be used, modified or distributed without - * prior permission by the authors of Eressea. - */ - -#include -#include -#include "alp.h" - -#include -#include -#include -#include -#include -#include - -/* util includes */ -#include -#include -#include -#include - -#include "monster.h" - -#include - -#include -#include -#include -#include - -/* libc includes */ -#include -#include -#include - -extern const char *directions[]; - -typedef struct alp_data { - unit *mage; - unit *target; // TODO: remove, use attribute-owner? -} alp_data; - -static void alp_init(attrib * a) -{ - a->data.v = calloc(sizeof(alp_data), 1); -} - -static void alp_done(attrib * a) -{ - free(a->data.v); -} - -static int alp_verify(attrib * a, void *owner) -{ - alp_data *ad = (alp_data *)a->data.v; - unused_arg(owner); - if (ad->mage && ad->target) - return 1; - return 0; /* remove the attribute */ -} - -static void -alp_write(const attrib * a, const void *owner, struct storage *store) -{ - alp_data *ad = (alp_data *)a->data.v; - write_unit_reference(ad->mage, store); - write_unit_reference(ad->target, store); -} - -static int alp_read(attrib * a, void *owner, struct storage *store) -{ - alp_data *ad = (alp_data *)a->data.v; - int rm = read_reference(&ad->mage, store, read_unit_reference, resolve_unit); - int rt = - read_reference(&ad->target, store, read_unit_reference, resolve_unit); - if (rt == 0 && rm == 0 && (!ad->target || !ad->mage)) { - /* the target or mage disappeared. */ - return AT_READ_FAIL; - } - return AT_READ_OK; -} - -static attrib_type at_alp = { - "alp", - alp_init, - alp_done, - alp_verify, - alp_write, - alp_read, - ATF_UNIQUE -}; - -int sp_summon_alp(struct castorder *co) -{ - unit *alp, *opfer; - region *r = co_get_region(co); - unit *mage = co->magician.u; - int cast_level = co->level; - spellparameter *pa = co->par; - const struct race *rc = get_race(RC_ALP); - struct faction *f = get_monsters(); - struct message *msg; - - opfer = pa->param[0]->data.u; - - /* Der Alp gehört den Monstern, darum erhält der Magier auch keine - * Regionsberichte von ihm. Er erhält aber später eine Mitteilung, - * sobald der Alp sein Opfer erreicht hat. - */ - alp = create_unit(r, f, 1, rc, 0, NULL, NULL); - set_level(alp, SK_STEALTH, 7); - setstatus(alp, ST_FLEE); /* flieht */ - - { - attrib *a = a_add(&alp->attribs, a_new(&at_alp)); - alp_data *ad = (alp_data *)a->data.v; - ad->mage = mage; - ad->target = opfer; - } - - { - /* Wenn der Alp stirbt, den Magier nachrichtigen */ - add_trigger(&alp->attribs, "destroy", trigger_unitmessage(mage, - "trigger_alp_destroy", MSG_EVENT, ML_INFO)); - /* Wenn Opfer oder Magier nicht mehr existieren, dann stirbt der Alp */ - add_trigger(&mage->attribs, "destroy", trigger_killunit(alp)); - add_trigger(&opfer->attribs, "destroy", trigger_killunit(alp)); - } - msg = msg_message("summon_alp_effect", "mage alp target", mage, alp, opfer); - r_addmessage(r, mage->faction, msg); - msg_release(msg); - - return cast_level; -} - -void alp_findet_opfer(unit * alp, region * r) -{ - curse *c; - attrib *a = a_find(alp->attribs, &at_alp); - alp_data *ad = (alp_data *)a->data.v; - unit *mage = ad->mage; - unit *opfer = ad->target; - float effect; - message *msg; - - assert(opfer); - assert(mage); - - /* Magier und Opfer Bescheid geben */ - msg = msg_message("alp_success", "target", opfer); - add_message(&mage->faction->msgs, msg); - r_addmessage(opfer->region, opfer->faction, msg); - msg_release(msg); - - /* Relations werden in destroy_unit(alp) automatisch gelöscht. - * Die Aktionen, die beim Tod des Alps ausgelöst werden sollen, - * müssen jetzt aber deaktiviert werden, sonst werden sie gleich - * beim destroy_unit(alp) ausgelöst. - */ - a_removeall(&alp->attribs, &at_eventhandler); - - /* Alp umwandeln in Curse */ - effect = -2; - c = - create_curse(mage, &opfer->attribs, ct_find("worse"), 2, 2, effect, - opfer->number); - /* solange es noch keine spezielle alp-Antimagie gibt, reagiert der - * auch auf normale */ - set_number(alp, 0); - - /* wenn der Magier stirbt, wird der Curse wieder vom Opfer genommen */ - add_trigger(&mage->attribs, "destroy", trigger_removecurse(c, opfer)); -} - -void register_alp(void) -{ - at_register(&at_alp); -} - -unit *alp_target(unit * alp) -{ - alp_data *ad; - unit *target = NULL; - - attrib *a = a_find(alp->attribs, &at_alp); - - if (a) { - ad = (alp_data *)a->data.v; - target = ad->target; - } - return target; - -} diff --git a/src/spells/alp.h b/src/spells/alp.h deleted file mode 100644 index f30cc1837..000000000 --- a/src/spells/alp.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * - * - * Eressea PB(E)M host Copyright (C) 1998-2003 - * Christian Schlittchen (corwin@amber.kn-bremen.de) - * Katja Zedel (katze@felidae.kn-bremen.de) - * Henning Peters (faroul@beyond.kn-bremen.de) - * Enno Rehling (enno@eressea.de) - * Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de) - * - * This program may not be used, modified or distributed without - * prior permission by the authors of Eressea. - */ - -#ifndef ALP_H -#define ALP_H -#ifdef __cplusplus -extern "C" { -#endif - - struct castorder; - struct region; - struct unit; - /* ------------------------------------------------------------- */ - /* Name: Alp - * Stufe: 15 - * Gebiet: Illaun - * Wirkung: - * Erschafft ein kleines Monster (den Alp). Dieser bewegt sich jede - * zweite Runde auf eine Zieleinheit zu. Sobald das Ziel erreicht ist, - * verwandelt es sich in einen Curse auf die Einheit, welches -2 auf - * alle Talente bewirkt. - * - * Fähigkeiten (factypes.c): - * Der Alp hat mittlere Tarnung (T7) und exzellente Verteidigung - * (+20, 99% Magieresistenz, siehe factypes.c) - * - * TODO: Der Alp-Curse sollte sich durch besondere Antimagie (Tybied) - * entfernen lassen. - * - * (UNITSPELL | SEARCHGLOBAL | TESTRESISTANCE) - */ - - extern int sp_summon_alp(struct castorder *co); - extern void register_alp(void); - - struct unit *alp_target(struct unit *alp); - void alp_findet_opfer(struct unit *alp, struct region *r); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/spells/borders.c b/src/spells/borders.c index c197b93c2..2c222a9dd 100644 --- a/src/spells/borders.c +++ b/src/spells/borders.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -51,8 +52,9 @@ typedef struct bresolve { static int resolve_buddy(variant data, void *addr); -static int cw_read(attrib * a, void *target, storage * store) +static int cw_read(attrib * a, void *target, gamedata *data) { + storage *store = data->store; bresolve *br = calloc(sizeof(bresolve), 1); curse *c = (curse *)a->data.v; wallcurse *wc = (wallcurse *)c->data.v; @@ -112,6 +114,7 @@ static attrib_type at_cursewall = { curse_age, cw_write, cw_read, + NULL, ATF_CURSE }; @@ -171,15 +174,15 @@ static void wall_destroy(connection * b) free(b->data.v); } -static void wall_read(connection * b, storage * store) +static void wall_read(connection * b, gamedata * data) { static wall_data dummy; wall_data *fd = b->data.v ? (wall_data *)b->data.v : &dummy; - read_reference(&fd->mage, store, read_unit_reference, resolve_unit); - READ_INT(store, &fd->force); - if (global.data_version >= NOBORDERATTRIBS_VERSION) { - READ_INT(store, &fd->countdown); + read_reference(&fd->mage, data, read_unit_reference, resolve_unit); + READ_INT(data->store, &fd->force); + if (data->version >= NOBORDERATTRIBS_VERSION) { + READ_INT(data->store, &fd->countdown); } fd->active = true; } diff --git a/src/spells/flyingship.c b/src/spells/flyingship.c index e8574db2b..c58ec270c 100644 --- a/src/spells/flyingship.c +++ b/src/spells/flyingship.c @@ -11,6 +11,8 @@ #include #include +#include + #include #include @@ -95,11 +97,11 @@ int sp_flying_ship(castorder * co) return cast_level; } -static int flyingship_read(storage * store, curse * c, void *target) +static int flyingship_read(gamedata * data, curse * c, void *target) { ship *sh = (ship *)target; c->data.v = sh; - if (global.data_version < FOSS_VERSION) { + if (data->version < FOSS_VERSION) { sh->flags |= SF_FLYING; return 0; } diff --git a/src/spells/flyingship.test.c b/src/spells/flyingship.test.c index b3d03f01b..98e524926 100644 --- a/src/spells/flyingship.test.c +++ b/src/spells/flyingship.test.c @@ -44,19 +44,23 @@ static void test_flyingship(CuTest * tc) sh1 = test_create_ship(r, shipType1); par_data.data.sh = sh1; - test_create_castorder(&co, u, 10, 10.0, 0, &par); + test_create_castorder(&co, u, 10, 10.0, 0, &par); CuAssertTrue(tc, !flying_ship(sh1)); CuAssertIntEquals(tc, 10, sp_flying_ship(&co)); CuAssertTrue(tc, flying_ship(sh1)); + co.par = 0; + free_castorder(&co); sh2 = test_create_ship(r, shipType2); par_data.data.sh = sh2; - test_create_castorder(&co, u, 10, 10.0, 0, &par); + test_create_castorder(&co, u, 10, 10.0, 0, &par); CuAssertTrue(tc, !flying_ship(sh2)); CuAssertIntEquals(tc, 0, sp_flying_ship(&co)); CuAssertTrue(tc, !flying_ship(sh2)); + co.par = 0; + free_castorder(&co); test_cleanup(); } diff --git a/src/spells/unitcurse.c b/src/spells/unitcurse.c index 73925a55a..c29935479 100644 --- a/src/spells/unitcurse.c +++ b/src/spells/unitcurse.c @@ -25,6 +25,7 @@ #include /* util includes */ +#include #include #include #include @@ -307,10 +308,10 @@ static struct curse_type ct_oldrace = { * C_SKILL */ -static int read_skill(struct storage *store, curse * c, void *target) +static int read_skill(gamedata *data, curse * c, void *target) { int skill; - READ_INT(store, &skill); + READ_INT(data->store, &skill); c->data.i = skill; return 0; } diff --git a/src/spy.c b/src/spy.c index e25ff1e85..9b8fe4601 100644 --- a/src/spy.c +++ b/src/spy.c @@ -22,6 +22,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "laws.h" #include "move.h" #include "reports.h" +#include "study.h" /* kernel includes */ #include diff --git a/src/spy.test.c b/src/spy.test.c index a4e2f1151..b843306f1 100644 --- a/src/spy.test.c +++ b/src/spy.test.c @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -90,7 +91,8 @@ static void setup_sabotage(void) { test_cleanup(); lang = get_or_create_locale("de"); locale_setstring(lang, parameters[P_SHIP], "SCHIFF"); - test_create_world(); + locale_setstring(lang, parameters[P_ANY], "ALLE"); + init_parameters(lang); init_locales(); } @@ -100,7 +102,7 @@ static void test_sabotage_self(CuTest *tc) { order *ord; setup_sabotage(); - r = findregion(0, 0); + r = test_create_region(0, 0, 0); assert(r); u = test_create_unit(test_create_faction(NULL), r); assert(u && u->faction && u->region == r); @@ -122,7 +124,7 @@ static void test_sabotage_other_fail(CuTest *tc) { message *msg; setup_sabotage(); - r = findregion(0, 0); + r = test_create_region(0, 0, 0); assert(r); u = test_create_unit(test_create_faction(NULL), r); u2 = test_create_unit(test_create_faction(NULL), r); @@ -151,7 +153,7 @@ static void test_sabotage_other_success(CuTest *tc) { order *ord; setup_sabotage(); - r = findregion(0, 0); + r = test_create_region(0, 0, 0); assert(r); u = test_create_unit(test_create_faction(NULL), r); u2 = test_create_unit(test_create_faction(NULL), r); diff --git a/src/sqlite.c b/src/sqlite.c index d45ca0cf3..be5f501ac 100644 --- a/src/sqlite.c +++ b/src/sqlite.c @@ -123,7 +123,7 @@ static void update_faction(sqlite3 *db, const faction *f) { "INSERT INTO faction_data (faction_id, code, name, email, lang, turn)" " VALUES (?, ?, ?, ?, ?, ?)"; sqlite3_stmt *stmt = 0; - strncpy(code, itoa36(f->no), sizeof(code)); + strcpy(code, itoa36(f->no)); sqlite3_prepare_v2(db, sql, -1, &stmt, 0); sqlite3_bind_int(stmt, 1, f->subscription); sqlite3_bind_text(stmt, 2, code, -1, SQLITE_STATIC); diff --git a/src/stdafx.h b/src/stdafx.h deleted file mode 100644 index f6db3cac1..000000000 --- a/src/stdafx.h +++ /dev/null @@ -1 +0,0 @@ -/* empty, only used in non-msvc builds */ diff --git a/src/study.c b/src/study.c index acce1b6d6..f0a27606c 100644 --- a/src/study.c +++ b/src/study.c @@ -23,7 +23,9 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include "study.h" #include "move.h" +#include "monster.h" #include "alchemy.h" +#include "academy.h" #include #include @@ -47,6 +49,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include /* libc includes */ @@ -57,8 +60,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include -#define TEACHNUMBER 10 - static skill_t getskill(const struct locale *lang) { char token[128]; @@ -157,7 +158,7 @@ static void done_learning(struct attrib *a) const attrib_type at_learning = { "learning", - init_learning, done_learning, NULL, NULL, NULL, + init_learning, done_learning, NULL, NULL, NULL, NULL, ATF_UNIQUE }; @@ -206,7 +207,6 @@ teach_unit(unit * teacher, unit * student, int nteaching, skill_t sk, n = _min(n, nteaching); if (n != 0) { - const struct building_type *btype = bt_find("academy"); int index = 0; if (teach == NULL) { @@ -227,21 +227,18 @@ teach_unit(unit * teacher, unit * student, int nteaching, skill_t sk, } teach->value += n; - /* Solange Akademien groessenbeschraenkt sind, sollte Lehrer und - * Student auch in unterschiedlichen Gebaeuden stehen duerfen */ - if (active_building(teacher, btype) && active_building(student, btype)) { - int j = study_cost(student, sk); - j = _max(50, j * 2); - /* kann Einheit das zahlen? */ - if (get_pooled(student, get_resourcetype(R_SILVER), GET_DEFAULT, j) >= j) { + if (student->building && teacher->building == student->building) { + /* Solange Akademien groessenbeschraenkt sind, sollte Lehrer und + * Student auch in unterschiedlichen Gebaeuden stehen duerfen */ + if (academy_can_teach(teacher, student, sk)) { /* Jeder Schueler zusaetzlich +10 Tage wenn in Uni. */ teach->value += (n / 30) * 10; /* learning erhoehen */ - /* Lehrer zusaetzlich +1 Tag pro Schueler. */ - if (academy) + /* Lehrer zusaetzlich +1 Tag pro Schueler. */ + if (academy) { *academy += n; - } /* sonst nehmen sie nicht am Unterricht teil */ + } + } } - /* Teaching ist die Anzahl Leute, denen man noch was beibringen kann. Da * hier nicht n verwendet wird, werden die Leute gezaehlt und nicht die * effektiv gelernten Tage. -> FALSCH ? (ENNO) @@ -283,7 +280,7 @@ int teach_cmd(unit * u, struct order *ord) static const curse_type *gbdream_ct = NULL; plane *pl; region *r = u->region; - skill_t sk = NOSKILL; + skill_t sk_academy = NOSKILL; int teaching, i, j, count, academy = 0; if (gbdream_ct == 0) @@ -325,6 +322,7 @@ int teach_cmd(unit * u, struct order *ord) #if TEACH_ALL if (getparam(u->faction->locale) == P_ANY) { + skill_t sk; unit *student; skill_t teachskill[MAXSKILLS]; int t = 0; @@ -383,6 +381,7 @@ int teach_cmd(unit * u, struct order *ord) init_order(ord); while (!parser_end()) { + skill_t sk; unit *u2; bool feedback; @@ -475,16 +474,15 @@ int teach_cmd(unit * u, struct order *ord) continue; } } - + sk_academy = sk; teaching -= teach_unit(u, u2, teaching, sk, false, &academy); } new_order = create_order(K_TEACH, u->faction->locale, "%s", zOrder); replace_order(&u->orders, ord, new_order); free_order(new_order); /* parse_order & set_order have each increased the refcount */ } - if (academy && sk != NOSKILL) { - academy = academy / 30; /* anzahl gelehrter wochen, max. 10 */ - learn_skill(u, sk, academy / 30.0 / TEACHNUMBER); + if (academy && sk_academy!=NOSKILL) { + academy_teaching_bonus(u, sk_academy, academy); } return 0; } @@ -742,17 +740,7 @@ int study_cmd(unit * u, order * ord) if (fval(u, UFL_HUNGER)) days /= 2; - while (days) { - if (days >= u->number * 30) { - learn_skill(u, sk, 1.0); - days -= u->number * 30; - } - else { - double chance = (double)days / u->number / 30; - learn_skill(u, sk, chance); - days = 0; - } - } + learn_skill(u, sk, days); if (a != NULL) { int index = 0; while (teach->teachers[index] && index != MAXTEACHERS) { @@ -803,3 +791,93 @@ int study_cmd(unit * u, order * ord) return 0; } + +static int produceexp_days(void) { + return config_get_int("study.produceexp", 10); +} + +void produceexp_ex(struct unit *u, skill_t sk, int n, learn_fun learn) +{ + assert(u && n <= u->number); + if (n > 0 && (is_monsters(u->faction) || playerrace(u_race(u)))) { + int days = produceexp_days(); + learn(u, sk, days * n / u->number); + } +} + +void produceexp(struct unit *u, skill_t sk, int n) +{ + produceexp_ex(u, sk, n, learn_skill); +} + +#ifndef NO_TESTS +static learn_fun inject_learn_fun = 0; + +void inject_learn(learn_fun fun) { + inject_learn_fun = fun; +} +#endif +void learn_skill(unit *u, skill_t sk, int days) { + int leveldays = STUDYDAYS * u->number; + int weeks = 0; +#ifndef NO_TESTS + if (inject_learn_fun) { + inject_learn_fun(u, sk, days); + return; + } +#endif + while (days >= leveldays) { + ++weeks; + days -= leveldays; + } + if (days > 0 && rng_int() % leveldays < days) { + ++weeks; + } + if (weeks > 0) { + skill *sv = unit_skill(u, sk); + if (!sv) { + sv = add_skill(u, sk); + } + while (sv->weeks <= weeks) { + weeks -= sv->weeks; + sk_set(sv, sv->level + 1); + } + sv->weeks -= weeks; + } +} + +/** Talente von Dämonen verschieben sich. +*/ +void demon_skillchange(unit *u) +{ + skill *sv = u->skills; + int upchance = 15; + int downchance = 10; + + if (fval(u, UFL_HUNGER)) { + /* hungry demons only go down, never up in skill */ + int rule_hunger = config_get_int("hunger.demon.skill", 0) != 0; + if (rule_hunger) { + upchance = 0; + downchance = 15; + } + } + + while (sv != u->skills + u->skill_size) { + int roll = rng_int() % 100; + if (sv->level > 0 && roll < upchance + downchance) { + int weeks = 1 + rng_int() % 3; + if (roll < downchance) { + reduce_skill(u, sv, weeks); + if (sv->level < 1) { + /* demons should never forget below 1 */ + set_level(u, sv->id, 1); + } + } + else { + learn_skill(u, sv->id, STUDYDAYS*weeks); + } + } + ++sv; + } +} diff --git a/src/study.h b/src/study.h index 1feb55921..e136d7345 100644 --- a/src/study.h +++ b/src/study.h @@ -26,14 +26,27 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. extern "C" { #endif - extern int teach_cmd(struct unit *u, struct order *ord); - extern int study_cmd(struct unit *u, struct order *ord); + struct unit; - extern magic_t getmagicskill(const struct locale *lang); - extern bool is_migrant(struct unit *u); - extern int study_cost(struct unit *u, skill_t talent); + int teach_cmd(struct unit *u, struct order *ord); + int study_cmd(struct unit *u, struct order *ord); + + magic_t getmagicskill(const struct locale *lang); + bool is_migrant(struct unit *u); + int study_cost(struct unit *u, skill_t talent); + + typedef void(*learn_fun)(struct unit *u, skill_t sk, int days); + +#define STUDYDAYS 30 + void learn_skill(struct unit *u, skill_t sk, int days); + + void produceexp(struct unit *u, skill_t sk, int n); + void produceexp_ex(struct unit *u, skill_t sk, int n, learn_fun learn); + + void demon_skillchange(struct unit *u); #define MAXTEACHERS 20 +#define TEACHNUMBER 10 typedef struct teaching_info { struct unit *teachers[MAXTEACHERS]; int value; @@ -41,6 +54,10 @@ extern "C" { extern const struct attrib_type at_learning; +#ifndef NO_TESTS + void inject_learn(learn_fun fun); +#endif + #ifdef __cplusplus } #endif diff --git a/src/study.test.c b/src/study.test.c index bdbfb9a01..7e0eb8744 100644 --- a/src/study.test.c +++ b/src/study.test.c @@ -3,10 +3,14 @@ #include "study.h" #include -#include +#include #include +#include #include +#include #include +#include +#include #include #include #include @@ -16,11 +20,50 @@ #include +#define MAXLOG 4 +typedef struct log_entry { + unit *u; + skill_t sk; + int days; +} log_entry; + +static log_entry log_learners[MAXLOG]; +static int log_size; + +static void log_learn(unit *u, skill_t sk, int days) { + if (log_size < MAXLOG) { + log_entry * entry = &log_learners[log_size++]; + entry->u = u; + entry->sk = sk; + entry->days = days; + } +} + +void learn_inject(void) { + log_size = 0; + inject_learn(log_learn); +} + +void learn_reset(void) { + inject_learn(0); +} + typedef struct { unit *u; unit *teachers[2]; } study_fixture; +static void setup_locale(struct locale *lang) { + int i; + for (i = 0; i < MAXSKILLS; ++i) { + if (!locale_getstring(lang, mkname("skill", skillnames[i]))) + locale_setstring(lang, mkname("skill", skillnames[i]), skillnames[i]); + } + locale_setstring(lang, parameters[P_ANY], "ALLE"); + init_parameters(lang); + init_skills(lang); +} + static void setup_study(study_fixture *fix, skill_t sk) { struct region * r; struct faction *f; @@ -33,8 +76,7 @@ static void setup_study(study_fixture *fix, skill_t sk) { r = findregion(0, 0); f = test_create_faction(0); lang = get_or_create_locale(locale_name(f->locale)); - locale_setstring(lang, mkname("skill", skillnames[sk]), skillnames[sk]); - init_skills(lang); + setup_locale(lang); fix->u = test_create_unit(f, r); assert(fix->u); fix->u->thisorder = create_order(K_STUDY, f->locale, "%s", skillnames[sk]); @@ -80,12 +122,10 @@ static void test_study_with_teacher(CuTest *tc) { static void test_study_with_bad_teacher(CuTest *tc) { study_fixture fix; skill *sv; - message *msg; setup_study(&fix, SK_CROSSBOW); teach_cmd(fix.teachers[0], fix.teachers[0]->thisorder); - CuAssertPtrNotNull(tc, msg = test_get_last_message(fix.u->faction->msgs)); - CuAssertStrEquals(tc, "teach_asgood", test_get_messagetype(msg)); + CuAssertPtrNotNull(tc, test_find_messagetype(fix.u->faction->msgs, "teach_asgood")); study_cmd(fix.u, fix.u->thisorder); CuAssertPtrNotNull(tc, sv = unit_skill(fix.u, SK_CROSSBOW)); CuAssertIntEquals(tc, 1, sv->level); @@ -93,11 +133,392 @@ static void test_study_with_bad_teacher(CuTest *tc) { test_cleanup(); } +static void test_study_bug_2194(CuTest *tc) { + unit *u, *u1, *u2; + struct locale * loc; + building * b; + + test_cleanup(); + random_source_inject_constant(0.0); + init_resources(); + loc = get_or_create_locale("de"); + setup_locale(loc); + u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); + scale_number(u, 2); + set_level(u, SK_CROSSBOW, TEACHDIFFERENCE); + u->faction->locale = loc; + u1 = test_create_unit(u->faction, u->region); + scale_number(u1, 17); + u1->thisorder = create_order(K_STUDY, loc, skillnames[SK_CROSSBOW]); + u2 = test_create_unit(u->faction, u->region); + scale_number(u2, 3); + u2->thisorder = create_order(K_STUDY, loc, skillnames[SK_MAGIC]); + u->thisorder = create_order(K_TEACH, loc, "%s %s", itoa36(u1->no), itoa36(u2->no)); + b = test_create_building(u->region, test_create_buildingtype("academy")); + b->size = 22; + u_set_building(u, b); + u_set_building(u1, b); + u_set_building(u2, b); + i_change(&u1->items, get_resourcetype(R_SILVER)->itype, 50); + i_change(&u2->items, get_resourcetype(R_SILVER)->itype, 50); + b->flags = BLD_MAINTAINED; + learn_inject(); + teach_cmd(u, u->thisorder); + learn_reset(); + CuAssertPtrEquals(tc, u, log_learners[0].u); + CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); + CuAssertIntEquals(tc, 1, log_size); + CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "teach_asgood")); + + free_order(u->thisorder); + u->thisorder = create_order(K_TEACH, loc, itoa36(u2->no)); + learn_inject(); + teach_cmd(u, u->thisorder); + learn_reset(); + CuAssertIntEquals(tc, 0, log_size); + test_cleanup(); +} + +static CuTest *g_tc; + +static void cb_learn_one(unit *u, skill_t sk, int days) { + CuAssertIntEquals(g_tc, SK_ALCHEMY, sk); + CuAssertIntEquals(g_tc, 10, days); +} + +static void cb_learn_two(unit *u, skill_t sk, int days) { + CuAssertIntEquals(g_tc, SK_ALCHEMY, sk); + CuAssertIntEquals(g_tc, 20, days); +} + +static void test_produceexp(CuTest *tc) { + unit *u; + + g_tc = tc; + test_cleanup(); + u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); + scale_number(u, 2); + config_set("study.produceexp", "20"); + produceexp_ex(u, SK_ALCHEMY, 1, cb_learn_one); + produceexp_ex(u, SK_ALCHEMY, 2, cb_learn_two); + test_cleanup(); +} + +static void test_academy_building(CuTest *tc) { + unit *u, *u1, *u2; + struct locale * loc; + building * b; + message * msg; + + test_cleanup(); + mt_register(mt_new_va("teach_asgood", "unit:unit", "region:region", "command:order", "student:unit", 0)); + + random_source_inject_constant(0.0); + init_resources(); + loc = get_or_create_locale("de"); + setup_locale(loc); + u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); + scale_number(u, 2); + set_level(u, SK_CROSSBOW, TEACHDIFFERENCE); + u->faction->locale = loc; + u1 = test_create_unit(u->faction, u->region); + scale_number(u1, 15); + u1->thisorder = create_order(K_STUDY, loc, skillnames[SK_CROSSBOW]); + u2 = test_create_unit(u->faction, u->region); + scale_number(u2, 5); + u2->thisorder = create_order(K_STUDY, loc, skillnames[SK_CROSSBOW]); + set_level(u2, SK_CROSSBOW, 1); + u->thisorder = create_order(K_TEACH, loc, "%s %s", itoa36(u1->no), itoa36(u2->no)); + b = test_create_building(u->region, test_create_buildingtype("academy")); + b->size = 22; + u_set_building(u, b); + u_set_building(u1, b); + u_set_building(u2, b); + i_change(&u1->items, get_resourcetype(R_SILVER)->itype, 50); + i_change(&u2->items, get_resourcetype(R_SILVER)->itype, 50); + b->flags = BLD_MAINTAINED; + learn_inject(); + teach_cmd(u, u->thisorder); + learn_reset(); + CuAssertPtrNotNull(tc, msg = test_find_messagetype(u->faction->msgs, "teach_asgood")); + CuAssertPtrEquals(tc, u, (unit *)(msg)->parameters[0].v); + CuAssertPtrEquals(tc, u2, (unit *)(msg)->parameters[3].v); + + CuAssertPtrEquals(tc, u, log_learners[0].u); + CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); + CuAssertIntEquals(tc, 15, log_learners[0].days); + test_cleanup(); +} + +void test_learn_skill_single(CuTest *tc) { + unit *u; + skill *sv; + test_cleanup(); + config_set("study.random_progress", "0"); + u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); + learn_skill(u, SK_ALCHEMY, STUDYDAYS); + CuAssertPtrNotNull(tc, sv = u->skills); + CuAssertIntEquals(tc, SK_ALCHEMY, sv->id); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, 2, sv->weeks); + learn_skill(u, SK_ALCHEMY, STUDYDAYS); + CuAssertIntEquals(tc, 1, sv->weeks); + learn_skill(u, SK_ALCHEMY, STUDYDAYS * 2); + CuAssertIntEquals(tc, 2, sv->level); + CuAssertIntEquals(tc, 2, sv->weeks); + test_cleanup(); +} + +void test_learn_skill_multi(CuTest *tc) { + unit *u; + skill *sv; + test_cleanup(); + config_set("study.random_progress", "0"); + u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); + scale_number(u, 10); + learn_skill(u, SK_ALCHEMY, STUDYDAYS * u->number); + CuAssertPtrNotNull(tc, sv = u->skills); + CuAssertIntEquals(tc, SK_ALCHEMY, sv->id); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, 2, sv->weeks); + learn_skill(u, SK_ALCHEMY, STUDYDAYS * u->number); + CuAssertIntEquals(tc, 1, sv->weeks); + learn_skill(u, SK_ALCHEMY, STUDYDAYS * u->number * 2); + CuAssertIntEquals(tc, 2, sv->level); + CuAssertIntEquals(tc, 2, sv->weeks); + test_cleanup(); +} + +static void test_demon_skillchanges(CuTest *tc) { + unit * u; + race * rc; + test_cleanup(); + rc = test_create_race("demon"); + CuAssertPtrEquals(tc, rc, get_race(RC_DAEMON)); + u = test_create_unit(test_create_faction(rc), 0); + CuAssertPtrNotNull(tc, u); + set_level(u, SK_CROSSBOW, 1); + demon_skillchange(u); + // TODO: sensing here + test_cleanup(); +} + +static void test_study_cmd(CuTest *tc) { + unit *u; + test_cleanup(); + init_resources(); + u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); + u->thisorder = create_order(K_STUDY, u->faction->locale, "CROSSBOW"); + learn_inject(); + study_cmd(u, u->thisorder); + learn_reset(); + CuAssertPtrEquals(tc, u, log_learners[0].u); + CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); + CuAssertIntEquals(tc, STUDYDAYS, log_learners[0].days); + test_cleanup(); +} + +static void test_study_cost(CuTest *tc) { + unit *u; + const struct item_type *itype; + test_cleanup(); + init_resources(); + itype = get_resourcetype(R_SILVER)->itype; + u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); + scale_number(u, 2); + u->thisorder = create_order(K_STUDY, u->faction->locale, "ALCHEMY"); + i_change(&u->items, itype, u->number * study_cost(u, SK_ALCHEMY)); + learn_inject(); + study_cmd(u, u->thisorder); + learn_reset(); + CuAssertPtrEquals(tc, u, log_learners[0].u); + CuAssertIntEquals(tc, SK_ALCHEMY, log_learners[0].sk); + CuAssertIntEquals(tc, STUDYDAYS*u->number, log_learners[0].days); + CuAssertIntEquals(tc, 0, i_get(u->items, itype)); + test_cleanup(); +} + +static void test_teach_cmd(CuTest *tc) { + unit *u, *ut; + test_cleanup(); + init_resources(); + u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); + scale_number(u, 10); + u->thisorder = create_order(K_STUDY, u->faction->locale, "CROSSBOW"); + ut = test_create_unit(u->faction, u->region); + set_level(ut, SK_CROSSBOW, TEACHDIFFERENCE); + ut->thisorder = create_order(K_TEACH, u->faction->locale, itoa36(u->no)); + learn_inject(); + teach_cmd(ut, ut->thisorder); + study_cmd(u, u->thisorder); + learn_reset(); + CuAssertPtrEquals(tc, u, log_learners[0].u); + CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); + CuAssertIntEquals(tc, STUDYDAYS*2*u->number, log_learners[0].days); + test_cleanup(); +} + +static void test_teach_two(CuTest *tc) { + unit *u1, *u2, *ut; + test_cleanup(); + init_resources(); + u1 = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); + scale_number(u1, 5); + u1->thisorder = create_order(K_STUDY, u1->faction->locale, "CROSSBOW"); + u2 = test_create_unit(u1->faction, u1->region); + scale_number(u2, 5); + u2->thisorder = create_order(K_STUDY, u2->faction->locale, "CROSSBOW"); + ut = test_create_unit(u1->faction, u1->region); + set_level(ut, SK_CROSSBOW, TEACHDIFFERENCE); + ut->thisorder = create_order(K_TEACH, ut->faction->locale, "%s %s", itoa36(u1->no), itoa36(u2->no)); + learn_inject(); + teach_cmd(ut, ut->thisorder); + study_cmd(u1, u1->thisorder); + study_cmd(u2, u2->thisorder); + learn_reset(); + CuAssertPtrEquals(tc, u1, log_learners[0].u); + CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); + CuAssertIntEquals(tc, STUDYDAYS * 2 * u1->number, log_learners[0].days); + CuAssertPtrEquals(tc, u2, log_learners[1].u); + CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[1].sk); + CuAssertIntEquals(tc, STUDYDAYS * 2 * u2->number, log_learners[1].days); + test_cleanup(); +} + +static void test_teach_two_skills(CuTest *tc) { + unit *u1, *u2, *ut; + faction *f; + region *r; + + test_cleanup(); + init_resources(); + f = test_create_faction(0); + r = test_create_region(0, 0, 0); + u1 = test_create_unit(f, r); + scale_number(u1, 5); + u1->thisorder = create_order(K_STUDY, f->locale, "CROSSBOW"); + u2 = test_create_unit(f, r); + scale_number(u2, 5); + u2->thisorder = create_order(K_STUDY, f->locale, "ENTERTAINMENT"); + ut = test_create_unit(f, r); + set_level(ut, SK_ENTERTAINMENT, TEACHDIFFERENCE); + set_level(ut, SK_CROSSBOW, TEACHDIFFERENCE); + ut->thisorder = create_order(K_TEACH, f->locale, "%s %s", itoa36(u1->no), itoa36(u2->no)); + learn_inject(); + teach_cmd(ut, ut->thisorder); + study_cmd(u1, u1->thisorder); + study_cmd(u2, u2->thisorder); + learn_reset(); + CuAssertPtrEquals(tc, u1, log_learners[0].u); + CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); + CuAssertIntEquals(tc, STUDYDAYS * 2 * u1->number, log_learners[0].days); + CuAssertPtrEquals(tc, u2, log_learners[1].u); + CuAssertIntEquals(tc, SK_ENTERTAINMENT, log_learners[1].sk); + CuAssertIntEquals(tc, STUDYDAYS * 2 * u2->number, log_learners[1].days); + test_cleanup(); +} + +static void test_teach_one_to_many(CuTest *tc) { + unit *u, *ut; + test_cleanup(); + init_resources(); + u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); + scale_number(u, 20); + u->thisorder = create_order(K_STUDY, u->faction->locale, "CROSSBOW"); + ut = test_create_unit(u->faction, u->region); + set_level(ut, SK_CROSSBOW, TEACHDIFFERENCE); + ut->thisorder = create_order(K_TEACH, u->faction->locale, itoa36(u->no)); + learn_inject(); + teach_cmd(ut, ut->thisorder); + study_cmd(u, u->thisorder); + learn_reset(); + CuAssertPtrEquals(tc, u, log_learners[0].u); + CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); + CuAssertIntEquals(tc, STUDYDAYS * 10 + STUDYDAYS * u->number, log_learners[0].days); + test_cleanup(); +} + +static void test_teach_many_to_one(CuTest *tc) { + unit *u, *u1, *u2; + test_cleanup(); + init_resources(); + u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); + scale_number(u, 20); + u->thisorder = create_order(K_STUDY, u->faction->locale, "CROSSBOW"); + u1 = test_create_unit(u->faction, u->region); + set_level(u1, SK_CROSSBOW, TEACHDIFFERENCE); + u1->thisorder = create_order(K_TEACH, u->faction->locale, itoa36(u->no)); + u2 = test_create_unit(u->faction, u->region); + set_level(u2, SK_CROSSBOW, TEACHDIFFERENCE); + u2->thisorder = create_order(K_TEACH, u->faction->locale, itoa36(u->no)); + learn_inject(); + teach_cmd(u1, u1->thisorder); + teach_cmd(u2, u2->thisorder); + study_cmd(u, u->thisorder); + learn_reset(); + CuAssertPtrEquals(tc, u, log_learners[0].u); + CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); + CuAssertIntEquals(tc, 2 * STUDYDAYS * u->number, log_learners[0].days); + test_cleanup(); +} + +static void test_teach_many_to_many(CuTest *tc) { + unit *s1, *s2, *t1, *t2; + region *r; + faction *f; + + test_cleanup(); + init_resources(); + f = test_create_faction(0); + r = test_create_region(0, 0, 0); + s1 = test_create_unit(f, r); + scale_number(s1, 20); + s1->thisorder = create_order(K_STUDY, f->locale, "CROSSBOW"); + s2 = test_create_unit(f, r); + scale_number(s2, 10); + s2->thisorder = create_order(K_STUDY, f->locale, "CROSSBOW"); + + t1 = test_create_unit(f, r); + set_level(t1, SK_CROSSBOW, TEACHDIFFERENCE); + t1->thisorder = create_order(K_TEACH, f->locale, "%s %s", itoa36(s1->no), itoa36(s2->no)); + t2 = test_create_unit(f, r); + scale_number(t2, 2); + set_level(t2, SK_CROSSBOW, TEACHDIFFERENCE); + t2->thisorder = create_order(K_TEACH, f->locale, "%s %s", itoa36(s1->no), itoa36(s2->no)); + learn_inject(); + teach_cmd(t1, t1->thisorder); + teach_cmd(t2, t2->thisorder); + study_cmd(s1, s1->thisorder); + study_cmd(s2, s2->thisorder); + learn_reset(); + CuAssertPtrEquals(tc, s1, log_learners[0].u); + CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); + CuAssertIntEquals(tc, 2 * STUDYDAYS * s1->number, log_learners[0].days); + CuAssertPtrEquals(tc, s2, log_learners[1].u); + CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[1].sk); + CuAssertIntEquals(tc, 2 * STUDYDAYS * s2->number, log_learners[1].days); + test_cleanup(); +} + CuSuite *get_study_suite(void) { CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_study_cmd); + SUITE_ADD_TEST(suite, test_study_cost); + SUITE_ADD_TEST(suite, test_teach_cmd); + SUITE_ADD_TEST(suite, test_teach_two); + SUITE_ADD_TEST(suite, test_teach_one_to_many); + SUITE_ADD_TEST(suite, test_teach_many_to_one); + SUITE_ADD_TEST(suite, test_teach_many_to_many); + SUITE_ADD_TEST(suite, test_teach_two_skills); + SUITE_ADD_TEST(suite, test_learn_skill_single); + SUITE_ADD_TEST(suite, test_learn_skill_multi); SUITE_ADD_TEST(suite, test_study_no_teacher); SUITE_ADD_TEST(suite, test_study_with_teacher); SUITE_ADD_TEST(suite, test_study_with_bad_teacher); + SUITE_ADD_TEST(suite, test_produceexp); + SUITE_ADD_TEST(suite, test_academy_building); + SUITE_ADD_TEST(suite, test_demon_skillchanges); + SUITE_ADD_TEST(suite, test_study_bug_2194); return suite; } diff --git a/src/summary.c b/src/summary.c index 824b86238..6378f3d7f 100644 --- a/src/summary.c +++ b/src/summary.c @@ -155,7 +155,7 @@ static void writeturn(void) char zText[MAX_PATH]; FILE *f; - sprintf(zText, "%s/datum", basepath()); + join_path(basepath(), "datum", zText, sizeof(zText)); f = fopen(zText, "w"); if (!f) { perror(zText); @@ -163,7 +163,7 @@ static void writeturn(void) } fputs(gamedate2(default_locale), f); fclose(f); - sprintf(zText, "%s/turn", basepath()); + join_path(basepath(), "turn", zText, sizeof(zText)); f = fopen(zText, "w"); if (!f) { perror(zText); @@ -181,10 +181,10 @@ void report_summary(summary * s, summary * o, bool full) char zText[MAX_PATH]; if (full) { - sprintf(zText, "%s/parteien.full", basepath()); + join_path(basepath(), "parteien.full", zText, sizeof(zText)); } else { - sprintf(zText, "%s/parteien", basepath()); + join_path(basepath(), "parteien", zText, sizeof(zText)); } F = fopen(zText, "w"); if (!F) { diff --git a/src/test_eressea.c b/src/test_eressea.c index a628d9009..4ea68180c 100644 --- a/src/test_eressea.c +++ b/src/test_eressea.c @@ -58,10 +58,6 @@ bool list = false; int RunAllTests(int argc, char *argv[]) { - int flags = log_flags; - - log_flags = LOG_FLUSH | LOG_CPERROR; - /* self-test */ ADD_SUITE(tests); ADD_SUITE(callback); @@ -79,6 +75,7 @@ int RunAllTests(int argc, char *argv[]) ADD_SUITE(base36); ADD_SUITE(bsdstring); ADD_SUITE(functions); + ADD_SUITE(gamedata); ADD_SUITE(parser); ADD_SUITE(password); ADD_SUITE(umlaut); @@ -90,6 +87,8 @@ int RunAllTests(int argc, char *argv[]) ADD_SUITE(xerewards); /* kernel */ ADD_SUITE(alliance); + ADD_SUITE(command); + ADD_SUITE(plane); ADD_SUITE(unit); ADD_SUITE(faction); ADD_SUITE(group); @@ -123,6 +122,7 @@ int RunAllTests(int argc, char *argv[]) ADD_SUITE(monsters); ADD_SUITE(move); ADD_SUITE(piracy); + ADD_SUITE(key); ADD_SUITE(stealth); ADD_SUITE(otherfaction); ADD_SUITE(upkeep); @@ -147,7 +147,6 @@ int RunAllTests(int argc, char *argv[]) suites = s; } printf("\ntest summary: %d tests, %d failed\n", summary->count, summary->failCount); - log_flags = flags; fail_count = summary->failCount; CuSuiteDelete(summary); game_done(); diff --git a/src/tests.c b/src/tests.c index b52759489..120d62e12 100644 --- a/src/tests.c +++ b/src/tests.c @@ -3,8 +3,10 @@ #include "keyword.h" #include "seen.h" #include "prefix.h" +#include "reports.h" #include +#include #include #include #include @@ -53,7 +55,7 @@ struct region *test_create_region(int x, int y, const terrain_type *terrain) if (!terrain) { terrain_type *t = get_or_create_terrain("plain"); t->size = 1000; - fset(t, LAND_REGION|CAVALRY_REGION|FOREST_REGION); + fset(t, LAND_REGION|CAVALRY_REGION|FOREST_REGION|FLY_INTO|WALK_INTO); terraform_region(r, t); } else { @@ -67,9 +69,48 @@ struct region *test_create_region(int x, int y, const terrain_type *terrain) return r; } +struct locale * test_create_locale(void) { + struct locale *loc = get_locale("test"); + if (!loc) { + int i; + loc = get_or_create_locale("test"); + locale_setstring(loc, "factiondefault", parameters[P_FACTION]); + for (i = 0; i < MAXSKILLS; ++i) { + if (!locale_getstring(loc, mkname("skill", skillnames[i]))) + locale_setstring(loc, mkname("skill", skillnames[i]), skillnames[i]); + } + for (i = 0; i != ALLIANCE_MAX; ++i) { + locale_setstring(loc, alliance_kwd[i], alliance_kwd[i]); + } + for (i = 0; i != MAXDIRECTIONS; ++i) { + locale_setstring(loc, directions[i], directions[i]); + init_direction(loc, i, directions[i]); + init_direction(loc, i, coasts[i]+7); + } + for (i = 0; i <= ST_FLEE; ++i) { + locale_setstring(loc, combatstatus[i], combatstatus[i]+7); + } + for (i = 0; i != MAXKEYWORDS; ++i) { + locale_setstring(loc, mkname("keyword", keywords[i]), keywords[i]); + } + for (i = 0; i != MAXSKILLS; ++i) { + locale_setstring(loc, mkname("skill", skillnames[i]), skillnames[i]); + } + for (i = 0; i != MAXPARAMS; ++i) { + locale_setstring(loc, parameters[i], parameters[i]); + test_translate_param(loc, i, parameters[i]); + } + init_parameters(loc); + init_keywords(loc); + init_skills(loc); + } + return loc; +} + struct faction *test_create_faction(const struct race *rc) { - faction *f = addfaction("nobody@eressea.de", NULL, rc ? rc : test_create_race("human"), default_locale, 0); + struct locale * loc = test_create_locale(); + faction *f = addfaction("nobody@eressea.de", NULL, rc ? rc : test_create_race("human"), loc, 0); test_clear_messages(f); return f; } @@ -78,6 +119,7 @@ struct unit *test_create_unit(struct faction *f, struct region *r) { const struct race * rc = f ? f->race : 0; assert(f || !r); + if (!rc) rc = rc_get_or_create("human"); return create_unit(r, f, 1, rc ? rc : rc_get_or_create("human"), 0, 0, 0); } @@ -85,6 +127,7 @@ void test_cleanup(void) { int i; + default_locale = 0; free_gamedata(); free_terrains(); free_resources(); @@ -154,11 +197,14 @@ ship_type * test_create_shiptype(const char * name) stype->construction->skill = SK_SHIPBUILDING; } + if (stype->coasts) { + free(stype->coasts); + } stype->coasts = - (terrain_type **)malloc(sizeof(terrain_type *)*2); - stype->coasts[0] = get_or_create_terrain("plain"); - stype->coasts[1] = NULL; - + (terrain_type **)malloc(sizeof(terrain_type *) * 3); + stype->coasts[0] = test_create_terrain("plain", LAND_REGION | FOREST_REGION | WALK_INTO | CAVALRY_REGION | FLY_INTO); + stype->coasts[1] = test_create_terrain("ocean", SEA_REGION | SWIM_INTO | FLY_INTO); + stype->coasts[2] = NULL; if (default_locale) { locale_setstring(default_locale, name, name); } @@ -282,10 +328,10 @@ void test_create_world(void) test_create_itemtype("iron"); test_create_itemtype("stone"); - t_plain = test_create_terrain("plain", LAND_REGION | FOREST_REGION | WALK_INTO | CAVALRY_REGION | SAIL_INTO | FLY_INTO); + t_plain = test_create_terrain("plain", LAND_REGION | FOREST_REGION | WALK_INTO | CAVALRY_REGION | FLY_INTO); t_plain->size = 1000; t_plain->max_road = 100; - t_ocean = test_create_terrain("ocean", SEA_REGION | SAIL_INTO | SWIM_INTO | FLY_INTO); + t_ocean = test_create_terrain("ocean", SEA_REGION | SWIM_INTO | FLY_INTO); t_ocean->size = 0; island[0] = test_create_region(0, 0, t_plain); @@ -350,6 +396,29 @@ void test_clear_messages(faction *f) { } } +void assert_message(CuTest * tc, message *msg, char *name, int numpar) { + const message_type *mtype = msg->type; + assert(mtype); + + CuAssertStrEquals(tc, name, mtype->name); + CuAssertIntEquals(tc, numpar, mtype->nparameters); +} + +void assert_pointer_parameter(CuTest * tc, message *msg, int index, void *arg) { + const message_type *mtype = (msg)->type; + CuAssertIntEquals((tc), VAR_VOIDPTR, mtype->types[(index)]->vtype);CuAssertPtrEquals((tc), (arg), msg->parameters[(index)].v); +} + +void assert_int_parameter(CuTest * tc, message *msg, int index, int arg) { + const message_type *mtype = (msg)->type; + CuAssertIntEquals((tc), VAR_INT, mtype->types[(index)]->vtype);CuAssertIntEquals((tc), (arg), msg->parameters[(index)].i); +} + +void assert_string_parameter(CuTest * tc, message *msg, int index, const char *arg) { + const message_type *mtype = (msg)->type; + CuAssertIntEquals((tc), VAR_VOIDPTR, mtype->types[(index)]->vtype);CuAssertStrEquals((tc), (arg), msg->parameters[(index)].v); +} + void disabled_test(void *suite, void (*test)(CuTest *), const char *name) { (void)test; fprintf(stderr, "%s: SKIP\n", name); diff --git a/src/tests.h b/src/tests.h index 30fd0fad1..d18ff6456 100644 --- a/src/tests.h +++ b/src/tests.h @@ -25,11 +25,13 @@ extern "C" { struct castorder; struct spellparameter; struct spell; + struct locale; struct CuTest; void test_cleanup(void); + struct locale * test_create_locale(void); struct terrain_type * test_create_terrain(const char * name, unsigned int flags); struct race *test_create_race(const char *name); struct region *test_create_region(int x, int y, @@ -52,6 +54,11 @@ extern "C" { struct message * test_find_messagetype(struct message_list *msgs, const char *name); struct message * test_get_last_message(struct message_list *mlist); void test_clear_messages(struct faction *f); + void assert_message(struct CuTest * tc, struct message *msg, char *name, int numpar); + + void assert_pointer_parameter(struct CuTest * tc, struct message *msg, int index, void *arg); + void assert_int_parameter(struct CuTest * tc, struct message *msg, int index, int arg); + void assert_string_parameter(struct CuTest * tc, struct message *msg, int index, const char *arg); void disabled_test(void *suite, void (*)(struct CuTest *), const char *name); diff --git a/src/triggers/CMakeLists.txt b/src/triggers/CMakeLists.txt index 76e7bf02f..18561f521 100644 --- a/src/triggers/CMakeLists.txt +++ b/src/triggers/CMakeLists.txt @@ -8,7 +8,6 @@ createunit.c gate.c giveitem.c killunit.c -removecurse.c shock.c timeout.c triggers.c diff --git a/src/triggers/changefaction.c b/src/triggers/changefaction.c index 47eee5874..13785dfb9 100644 --- a/src/triggers/changefaction.c +++ b/src/triggers/changefaction.c @@ -28,6 +28,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* util includes */ #include #include +#include #include #include #include @@ -82,12 +83,13 @@ static void changefaction_write(const trigger * t, struct storage *store) write_faction_reference(td->faction->_alive ? td->faction : NULL, store); } -static int changefaction_read(trigger * t, struct storage *store) +static int changefaction_read(trigger * t, gamedata *data) { variant var; changefaction_data *td = (changefaction_data *)t->data.v; - read_reference(&td->unit, store, read_unit_reference, resolve_unit); - var = read_faction_reference(store); + + read_reference(&td->unit, data, read_unit_reference, resolve_unit); + var = read_faction_reference(data); if (var.i == 0) { return AT_READ_FAIL; } diff --git a/src/triggers/changerace.c b/src/triggers/changerace.c index d4937cc9f..0e7fc997e 100644 --- a/src/triggers/changerace.c +++ b/src/triggers/changerace.c @@ -27,6 +27,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* util includes */ #include #include +#include #include #include #include @@ -86,12 +87,12 @@ static void changerace_write(const trigger * t, struct storage *store) write_race_reference(td->irace, store); } -static int changerace_read(trigger * t, struct storage *store) +static int changerace_read(trigger * t, gamedata *data) { changerace_data *td = (changerace_data *)t->data.v; - read_reference(&td->u, store, read_unit_reference, resolve_unit); - td->race = (const struct race *)read_race_reference(store).v; - td->irace = (const struct race *)read_race_reference(store).v; + read_reference(&td->u, data, read_unit_reference, resolve_unit); + td->race = (const struct race *)read_race_reference(data->store).v; + td->irace = (const struct race *)read_race_reference(data->store).v; return AT_READ_OK; } diff --git a/src/triggers/clonedied.c b/src/triggers/clonedied.c index 7a91ea514..37693fdbf 100644 --- a/src/triggers/clonedied.c +++ b/src/triggers/clonedied.c @@ -29,6 +29,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* util includes */ #include #include +#include #include #include #include @@ -68,10 +69,10 @@ static void clonedied_write(const trigger * t, struct storage *store) write_unit_reference(u, store); } -static int clonedied_read(trigger * t, struct storage *store) +static int clonedied_read(trigger * t, gamedata *data) { int result = - read_reference(&t->data.v, store, read_unit_reference, resolve_unit); + read_reference(&t->data.v, data, read_unit_reference, resolve_unit); if (result == 0 && t->data.v == NULL) { return AT_READ_FAIL; } diff --git a/src/triggers/createcurse.c b/src/triggers/createcurse.c index 07e8a370e..d0d65571b 100644 --- a/src/triggers/createcurse.c +++ b/src/triggers/createcurse.c @@ -28,6 +28,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* util includes */ #include #include +#include #include #include #include @@ -93,30 +94,30 @@ static void createcurse_write(const trigger * t, struct storage *store) WRITE_INT(store, td->men); } -static int createcurse_read(trigger * t, struct storage *store) +static int createcurse_read(trigger * t, gamedata *data) { createcurse_data *td = (createcurse_data *)t->data.v; char zText[128]; float flt; - read_reference(&td->mage, store, read_unit_reference, resolve_unit); - read_reference(&td->target, store, read_unit_reference, resolve_unit); + read_reference(&td->mage, data, read_unit_reference, resolve_unit); + read_reference(&td->target, data, read_unit_reference, resolve_unit); - READ_TOK(store, zText, sizeof(zText)); + READ_TOK(data->store, zText, sizeof(zText)); td->type = ct_find(zText); - READ_FLT(store, &flt); + READ_FLT(data->store, &flt); td->vigour = flt; - READ_INT(store, &td->duration); - if (global.data_version < CURSEFLOAT_VERSION) { + READ_INT(data->store, &td->duration); + if (data->version < CURSEFLOAT_VERSION) { int n; - READ_INT(store, &n); + READ_INT(data->store, &n); td->effect = (float)n; } else { - READ_FLT(store, &flt); + READ_FLT(data->store, &flt); td->effect = flt; } - READ_INT(store, &td->men); + READ_INT(data->store, &td->men); return AT_READ_OK; } diff --git a/src/triggers/createunit.c b/src/triggers/createunit.c index 45b67ec3a..6e98901ca 100644 --- a/src/triggers/createunit.c +++ b/src/triggers/createunit.c @@ -31,6 +31,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include #include @@ -88,13 +89,12 @@ static void createunit_write(const trigger * t, struct storage *store) WRITE_INT(store, td->number); } -static int createunit_read(trigger * t, struct storage *store) +static int createunit_read(trigger * t, gamedata *data) { createunit_data *td = (createunit_data *)t->data.v; variant var; int result = AT_READ_OK; - - var = read_faction_reference(store); + var = read_faction_reference(data); if (var.i > 0) { td->f = findfaction(var.i); if (!td->f) { @@ -105,13 +105,14 @@ static int createunit_read(trigger * t, struct storage *store) result = AT_READ_FAIL; } // read_reference(&td->f, store, read_faction_reference, resolve_faction); - read_reference(&td->r, store, read_region_reference, - RESOLVE_REGION(global.data_version)); - td->race = (const struct race *)read_race_reference(store).v; + + read_reference(&td->r, data, read_region_reference, + RESOLVE_REGION(data->version)); + td->race = (const struct race *)read_race_reference(data->store).v; if (!td->race) { result = AT_READ_FAIL; } - READ_INT(store, &td->number); + READ_INT(data->store, &td->number); return result; } diff --git a/src/triggers/gate.c b/src/triggers/gate.c index 44d26e0ef..2a9c5693d 100644 --- a/src/triggers/gate.c +++ b/src/triggers/gate.c @@ -13,7 +13,7 @@ #include #include "gate.h" -/* kernel includes */ + /* kernel includes */ #include #include #include @@ -22,6 +22,7 @@ /* util includes */ #include #include +#include #include #include @@ -72,15 +73,15 @@ static void gate_write(const trigger * t, struct storage *store) write_region_reference(r, store); } -static int gate_read(trigger * t, struct storage *store) +static int gate_read(trigger * t, gamedata *data) { gate_data *gd = (gate_data *)t->data.v; int bc = - read_reference(&gd->gate, store, read_building_reference, resolve_building); + read_reference(&gd->gate, data, read_building_reference, resolve_building); int rc = - read_reference(&gd->target, store, read_region_reference, - RESOLVE_REGION(global.data_version)); + read_reference(&gd->target, data, read_region_reference, + RESOLVE_REGION(data->version)); if (bc == 0 && rc == 0) { if (!gd->gate || !gd->target) diff --git a/src/triggers/giveitem.c b/src/triggers/giveitem.c index 02161775b..d4a56162b 100644 --- a/src/triggers/giveitem.c +++ b/src/triggers/giveitem.c @@ -28,6 +28,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include #include @@ -82,15 +83,15 @@ static void giveitem_write(const trigger * t, struct storage *store) WRITE_TOK(store, td->itype->rtype->_name); } -static int giveitem_read(trigger * t, struct storage *store) +static int giveitem_read(trigger * t, gamedata *data) { giveitem_data *td = (giveitem_data *)t->data.v; char zText[128]; - int result = read_reference(&td->u, store, read_unit_reference, resolve_unit); + int result = read_reference(&td->u, data, read_unit_reference, resolve_unit); - READ_INT(store, &td->number); - READ_TOK(store, zText, sizeof(zText)); + READ_INT(data->store, &td->number); + READ_TOK(data->store, zText, sizeof(zText)); td->itype = it_find(zText); assert(td->itype); diff --git a/src/triggers/killunit.c b/src/triggers/killunit.c index 866914835..23275a6af 100644 --- a/src/triggers/killunit.c +++ b/src/triggers/killunit.c @@ -27,6 +27,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include #include @@ -58,10 +59,10 @@ static void killunit_write(const trigger * t, struct storage *store) write_unit_reference(u, store); } -static int killunit_read(trigger * t, struct storage *store) +static int killunit_read(trigger * t, gamedata *data) { - int result = - read_reference(&t->data.v, store, read_unit_reference, resolve_unit); + int result = read_reference(&t->data.v, data, read_unit_reference, + resolve_unit); if (result == 0 && t->data.v == NULL) { return AT_READ_FAIL; } diff --git a/src/triggers/removecurse.c b/src/triggers/removecurse.c deleted file mode 100644 index 94819843a..000000000 --- a/src/triggers/removecurse.c +++ /dev/null @@ -1,106 +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 -#include "removecurse.h" - -/* kernel includes */ -#include -#include - -/* util includes */ -#include -#include -#include -#include -#include - -#include - -/* ansi includes */ -#include -#include -#include - -#include - -typedef struct removecurse_data { - curse *curse; - unit *target; -} removecurse_data; - -static void removecurse_init(trigger * t) -{ - t->data.v = calloc(sizeof(removecurse_data), 1); -} - -static void removecurse_free(trigger * t) -{ - free(t->data.v); -} - -static int removecurse_handle(trigger * t, void *data) -{ - /* call an event handler on removecurse. - * data.v -> ( variant event, int timer ) - */ - removecurse_data *td = (removecurse_data *)t->data.v; - if (td->curse && td->target) { - if (!remove_curse(&td->target->attribs, td->curse)) { - log_error("could not perform removecurse::handle()\n"); - } - } - unused_arg(data); - return 0; -} - -static void removecurse_write(const trigger * t, struct storage *store) -{ - removecurse_data *td = (removecurse_data *)t->data.v; - WRITE_TOK(store, td->target ? itoa36(td->target->no) : 0); - WRITE_INT(store, td->curse ? td->curse->no : 0); -} - -static int removecurse_read(trigger * t, struct storage *store) -{ - removecurse_data *td = (removecurse_data *)t->data.v; - - read_reference(&td->target, store, read_unit_reference, resolve_unit); - read_reference(&td->curse, store, read_int, resolve_curse); - - return AT_READ_OK; -} - -trigger_type tt_removecurse = { - "removecurse", - removecurse_init, - removecurse_free, - removecurse_handle, - removecurse_write, - removecurse_read -}; - -trigger *trigger_removecurse(curse * c, unit * target) -{ - trigger *t = t_new(&tt_removecurse); - removecurse_data *td = (removecurse_data *)t->data.v; - td->curse = c; - td->target = target; - return t; -} diff --git a/src/triggers/shock.c b/src/triggers/shock.c index 95e7b61b6..75904e346 100644 --- a/src/triggers/shock.c +++ b/src/triggers/shock.c @@ -33,6 +33,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include #include #include @@ -125,10 +126,10 @@ static void shock_write(const trigger * t, struct storage *store) } } -static int shock_read(trigger * t, struct storage *store) +static int shock_read(trigger * t, gamedata *data) { int result = - read_reference(&t->data.v, store, read_unit_reference, resolve_unit); + read_reference(&t->data.v, data, read_unit_reference, resolve_unit); if (result == 0 && t->data.v == NULL) { return AT_READ_FAIL; } diff --git a/src/triggers/shock.test.c b/src/triggers/shock.test.c index eae2a5004..77bc192dc 100644 --- a/src/triggers/shock.test.c +++ b/src/triggers/shock.test.c @@ -24,6 +24,8 @@ static void test_shock(CuTest *tc) { CuAssertIntEquals(tc, 2, u->hp); CuAssertIntEquals(tc, 2, get_spellpoints(u)); CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "shock")); + t_free(tt); + free(tt); test_cleanup(); } @@ -41,6 +43,8 @@ static void test_shock_low(CuTest *tc) { tt->type->handle(tt, u); CuAssertIntEquals(tc, 1, u->hp); CuAssertIntEquals(tc, 1, get_spellpoints(u)); + t_free(tt); + free(tt); test_cleanup(); } diff --git a/src/triggers/timeout.c b/src/triggers/timeout.c index 8f1778f21..06fa18363 100644 --- a/src/triggers/timeout.c +++ b/src/triggers/timeout.c @@ -23,6 +23,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* util includes */ #include #include +#include #include #include @@ -72,11 +73,11 @@ static void timeout_write(const trigger * t, struct storage *store) write_triggers(store, td->triggers); } -static int timeout_read(trigger * t, struct storage *store) +static int timeout_read(trigger * t, gamedata *data) { timeout_data *td = (timeout_data *)t->data.v; - READ_INT(store, &td->timer); - read_triggers(store, &td->triggers); + READ_INT(data->store, &td->timer); + read_triggers(data, &td->triggers); if (td->timer > 20) { trigger *tr = td->triggers; log_warning("there is a timeout lasting for another %d turns\n", td->timer); diff --git a/src/triggers/triggers.c b/src/triggers/triggers.c index 6a4bb780d..152fdcb58 100644 --- a/src/triggers/triggers.c +++ b/src/triggers/triggers.c @@ -28,7 +28,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include -#include #include #include #include @@ -50,7 +49,6 @@ void register_triggers(void) tt_register(&tt_unguard); tt_register(&tt_giveitem); tt_register(&tt_killunit); - tt_register(&tt_removecurse); tt_register(&tt_shock); tt_register(&tt_unitmessage); tt_register(&tt_timeout); diff --git a/src/triggers/unguard.c b/src/triggers/unguard.c index 49cb6fe25..e6471f977 100644 --- a/src/triggers/unguard.c +++ b/src/triggers/unguard.c @@ -22,6 +22,7 @@ /* util includes */ #include #include +#include #include #include @@ -48,10 +49,9 @@ static void unguard_write(const trigger * t, struct storage *store) write_building_reference((building *)t->data.v, store); } -static int unguard_read(trigger * t, struct storage *store) +static int unguard_read(trigger * t, gamedata *data) { - int rb = - read_reference(&t->data.v, store, read_building_reference, + int rb = read_reference(&t->data.v, data, read_building_reference, resolve_building); if (rb == 0 && !t->data.v) { return AT_READ_FAIL; diff --git a/src/triggers/unitmessage.c b/src/triggers/unitmessage.c index df227ed24..2d869fc49 100644 --- a/src/triggers/unitmessage.c +++ b/src/triggers/unitmessage.c @@ -20,6 +20,7 @@ without prior permission by the authors of Eressea. #include #include #include +#include #include #include #include @@ -84,17 +85,17 @@ static void unitmessage_write(const trigger * t, struct storage *store) WRITE_INT(store, td->level); } -static int unitmessage_read(trigger * t, struct storage *store) +static int unitmessage_read(trigger * t, gamedata *data) { unitmessage_data *td = (unitmessage_data *)t->data.v; char zText[256]; - int result = - read_reference(&td->target, store, read_unit_reference, resolve_unit); - READ_TOK(store, zText, sizeof(zText)); + int result = read_reference(&td->target, data, read_unit_reference, + resolve_unit); + READ_TOK(data->store, zText, sizeof(zText)); td->string = _strdup(zText); - READ_INT(store, &td->type); - READ_INT(store, &td->level); + READ_INT(data->store, &td->type); + READ_INT(data->store, &td->level); if (result == 0 && td->target == NULL) { return AT_READ_FAIL; diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index a34d6cbb0..46dce6801 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -1,17 +1,34 @@ project(util C) SET(_TEST_FILES +attrib.test.c base36.test.c +bsdstring.test.c +# crmessage.test.c +# dice.test.c +# event.test.c +# filereader.test.c +functions.test.c +gamedata.test.c +# goodies.test.c +# language.test.c +# lists.test.c +# log.test.c +# message.test.c +# nrmessage.test.c parser.test.c password.test.c -attrib.test.c +# rand.test.c +# resolve.test.c +rng.test.c strings.test.c bsdstring.test.c functions.test.c log.test.c +# translation.test.c umlaut.test.c unicode.test.c -rng.test.c +# xml.test.c ) SET(_FILES @@ -23,6 +40,7 @@ dice.c event.c filereader.c functions.c +gamedata.c goodies.c language.c lists.c diff --git a/src/util/attrib.c b/src/util/attrib.c index 9db46836a..c22570356 100644 --- a/src/util/attrib.c +++ b/src/util/attrib.c @@ -22,6 +22,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "log.h" #include "storage.h" +#include #include #include @@ -101,13 +102,6 @@ attrib *a_find(attrib * a, const attrib_type * at) return a; } -const attrib *a_findc(const attrib * a, const attrib_type * at) -{ - while (a && a->type != at) - a = a->nexttype; - return a; -} - static attrib *a_insert(attrib * head, attrib * a) { attrib **pa; @@ -216,28 +210,37 @@ int a_remove(attrib ** pa, attrib * a) void a_removeall(attrib ** pa, const attrib_type * at) { attrib **pnexttype = pa; - attrib **pnext = NULL; - while (*pnexttype) { - attrib *next = *pnexttype; - if (next->type == at) - break; - pnexttype = &next->nexttype; - pnext = &next->next; - } - if (*pnexttype && (*pnexttype)->type == at) { - attrib *a = *pnexttype; - - *pnexttype = a->nexttype; - if (pnext) { - while (*pnext && (*pnext)->type != at) - pnext = &(*pnext)->next; - *pnext = a->nexttype; + if (!at) { + while (*pnexttype) { + attrib *a = *pnexttype; + *pnexttype = a->next; + a_free(a); } - while (a && a->type == at) { - attrib *ra = a; - a = a->next; - a_free(ra); + } + else { + attrib **pnext = NULL; + while (*pnexttype) { + attrib *a = *pnexttype; + if (a->type == at) + break; + pnexttype = &a->nexttype; + pnext = &a->next; + } + if (*pnexttype && (*pnexttype)->type == at) { + attrib *a = *pnexttype; + + *pnexttype = a->nexttype; + if (pnext) { + while (*pnext && (*pnext)->type != at) + pnext = &(*pnext)->next; + *pnext = a->nexttype; + } + while (a && a->type == at) { + attrib *ra = a; + a = a->next; + a_free(ra); + } } } } @@ -274,69 +277,99 @@ int a_age(attrib ** p, void *owner) static critbit_tree cb_deprecated = { 0 }; -void at_deprecate(const char * name, int(*reader)(attrib *, void *, struct storage *)) + +typedef struct deprecated_s { + unsigned int hash; + int(*reader)(attrib *, void *, struct gamedata *); +} deprecated_t; + +void at_deprecate(const char * name, int(*reader)(attrib *, void *, struct gamedata *)) { - char buffer[64]; - size_t len = strlen(name); - len = cb_new_kv(name, len, &reader, sizeof(reader), buffer); - cb_insert(&cb_deprecated, buffer, len); + deprecated_t value; + + value.hash = __at_hashkey(name); + value.reader = reader; + cb_insert(&cb_deprecated, &value, sizeof(value)); } -int a_read(struct storage *store, attrib ** attribs, void *owner) +static int a_read_i(gamedata *data, attrib ** attribs, void *owner, unsigned int key) { + int retval = AT_READ_OK; + int(*reader)(attrib *, void *, struct gamedata *) = 0; + attrib_type *at = at_find(key); + attrib * na = 0; + + if (at) { + reader = at->read; + na = a_new(at); + } + else { + void *match; + if (cb_find_prefix(&cb_deprecated, &key, sizeof(key), &match, 1, 0)>0) { + deprecated_t *value = (deprecated_t *)match; + reader = value->reader; + } + else { + log_error("unknown attribute hash: %u\n", key); + assert(at || !"attribute not registered"); + } + } + if (reader) { + int ret = reader(na, owner, data); + if (na) { + switch (ret) { + case AT_READ_DEPR: + case AT_READ_OK: + a_add(attribs, na); + retval = ret; + break; + case AT_READ_FAIL: + a_free(na); + break; + default: + assert(!"invalid return value"); + break; + } + } + } + else { + assert(!"error: no registered callback can read attribute"); + } + return retval; +} + +int a_read(gamedata *data, attrib ** attribs, void *owner) { + struct storage *store = data->store; + int key, retval = AT_READ_OK; + + key = -1; + READ_INT(store, &key); + while (key > 0) { + int ret = a_read_i(data, attribs, owner, key); + if (ret == AT_READ_DEPR) { + retval = AT_READ_DEPR; + } + READ_INT(store, &key); + } + return retval; +} + +int a_read_orig(gamedata *data, attrib ** attribs, void *owner) { int key, retval = AT_READ_OK; char zText[128]; zText[0] = 0; key = -1; - READ_TOK(store, zText, sizeof(zText)); - if (strcmp(zText, "end") == 0) + READ_TOK(data->store, zText, sizeof(zText)); + if (strcmp(zText, "end") == 0) { return retval; - else + } + else { key = __at_hashkey(zText); - - while (key != -1) { - int(*reader)(attrib *, void *, struct storage *) = 0; - attrib_type *at = at_find(key); - attrib * na = 0; - - if (at) { - reader = at->read; - na = a_new(at); - } - else { - void * kv = 0; - cb_find_prefix(&cb_deprecated, zText, strlen(zText) + 1, &kv, 1, 0); - if (kv) { - cb_get_kv(kv, &reader, sizeof(reader)); - } - else { - fprintf(stderr, "attribute hash: %d (%s)\n", key, zText); - assert(at || !"attribute not registered"); - } - } - if (reader) { - int i = reader(na, owner, store); - if (na) { - switch (i) { - case AT_READ_OK: - a_add(attribs, na); - break; - case AT_READ_FAIL: - retval = AT_READ_FAIL; - a_free(na); - break; - default: - assert(!"invalid return value"); - break; - } - } - } - else { - assert(!"error: no registered callback can read attribute"); - } - - READ_TOK(store, zText, sizeof(zText)); + } + while (key > 0) { + retval = a_read_i(data, attribs, owner, key); + READ_TOK(data->store, zText, sizeof(zText)); if (!strcmp(zText, "end")) break; key = __at_hashkey(zText); @@ -344,7 +377,24 @@ int a_read(struct storage *store, attrib ** attribs, void *owner) return retval; } -void a_write(struct storage *store, const attrib * attribs, const void *owner) +void a_write(struct storage *store, const attrib * attribs, const void *owner) { + const attrib *na = attribs; + + while (na) { + if (na->type->write) { + assert(na->type->hashkey || !"attribute not registered"); + WRITE_INT(store, na->type->hashkey); + na->type->write(na, owner, store); + na = na->next; + } + else { + na = na->nexttype; + } + } + WRITE_INT(store, 0); +} + +void a_write_orig(struct storage *store, const attrib * attribs, const void *owner) { const attrib *na = attribs; diff --git a/src/util/attrib.h b/src/util/attrib.h index 2703de0cb..b41ac2bcf 100644 --- a/src/util/attrib.h +++ b/src/util/attrib.h @@ -55,7 +55,8 @@ extern "C" { int(*age) (struct attrib *, void *owner); /* age returns 0 if the attribute needs to be removed, !=0 otherwise */ void(*write) (const struct attrib *, const void *owner, struct storage *); - int(*read) (struct attrib *, void *owner, struct storage *); /* return AT_READ_OK on success, AT_READ_FAIL if attrib needs removal */ + int(*read) (struct attrib *, void *owner, struct gamedata *); /* return AT_READ_OK on success, AT_READ_FAIL if attrib needs removal */ + void(*upgrade) (struct attrib **alist, struct attrib *a); unsigned int flags; /* ---- internal data, do not modify: ---- */ struct attrib_type *nexthash; @@ -63,21 +64,22 @@ extern "C" { } attrib_type; extern void at_register(attrib_type * at); - extern void at_deprecate(const char * name, int(*reader)(attrib *, void *, struct storage *)); + extern void at_deprecate(const char * name, int(*reader)(attrib *, void *, struct gamedata *)); extern attrib *a_select(attrib * a, const void *data, bool(*compare) (const attrib *, const void *)); extern attrib *a_find(attrib * a, const attrib_type * at); - extern const attrib *a_findc(const attrib * a, const attrib_type * at); extern attrib *a_add(attrib ** pa, attrib * at); extern int a_remove(attrib ** pa, attrib * at); extern void a_removeall(attrib ** a, const attrib_type * at); extern attrib *a_new(const attrib_type * at); + int a_age(attrib ** attribs, void *owner); - extern int a_age(attrib ** attribs, void *owner); - extern int a_read(struct storage *store, attrib ** attribs, void *owner); - extern void a_write(struct storage *store, const attrib * attribs, - const void *owner); + int a_read_orig(struct gamedata *data, attrib ** attribs, void *owner); + void a_write_orig(struct storage *store, const attrib * attribs, const void *owner); + + int a_read(struct gamedata *data, attrib ** attribs, void *owner); + void a_write(struct storage *store, const attrib * attribs, const void *owner); void free_attribs(void); @@ -89,6 +91,7 @@ extern "C" { #define AT_READ_OK 0 #define AT_READ_FAIL -1 +#define AT_READ_DEPR 1 /* a deprecated attribute was read, should run a_upgrade */ #define AT_AGE_REMOVE 0 /* remove the attribute after calling age() */ #define AT_AGE_KEEP 1 /* keep the attribute for another turn */ diff --git a/src/util/attrib.test.c b/src/util/attrib.test.c index 691087477..0f189015a 100644 --- a/src/util/attrib.test.c +++ b/src/util/attrib.test.c @@ -49,6 +49,24 @@ static void test_attrib_remove_self(CuTest * tc) { CuAssertPtrEquals(tc, 0, alist->nexttype); CuAssertIntEquals(tc, 1, a_remove(&alist, alist)); CuAssertPtrEquals(tc, a, alist); + a_removeall(&alist, NULL); +} + + +static void test_attrib_removeall(CuTest * tc) { + const attrib_type at_foo = { "foo" }; + const attrib_type at_bar = { "bar" }; + attrib *alist = 0, *a; + a_add(&alist, a_new(&at_foo)); + a = a_add(&alist, a_new(&at_bar)); + a_add(&alist, a_new(&at_foo)); + a_removeall(&alist, &at_foo); + CuAssertPtrEquals(tc, a, alist); + CuAssertPtrEquals(tc, 0, alist->next); + a_add(&alist, a_new(&at_bar)); + a_add(&alist, a_new(&at_foo)); + a_removeall(&alist, NULL); + CuAssertPtrEquals(tc, 0, alist); } static void test_attrib_remove(CuTest * tc) @@ -98,6 +116,7 @@ CuSuite *get_attrib_suite(void) SUITE_ADD_TEST(suite, test_attrib_new); SUITE_ADD_TEST(suite, test_attrib_add); SUITE_ADD_TEST(suite, test_attrib_remove); + SUITE_ADD_TEST(suite, test_attrib_removeall); SUITE_ADD_TEST(suite, test_attrib_remove_self); SUITE_ADD_TEST(suite, test_attrib_nexttype); return suite; diff --git a/src/util/event.c b/src/util/event.c index 5784a116a..ad5a9e725 100644 --- a/src/util/event.c +++ b/src/util/event.c @@ -21,6 +21,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* util includes */ #include "attrib.h" +#include "gamedata.h" #include "log.h" #include "storage.h" @@ -42,26 +43,27 @@ void write_triggers(struct storage *store, const trigger * t) WRITE_TOK(store, "end"); } -int read_triggers(struct storage *store, trigger ** tp) +int read_triggers(struct gamedata *data, trigger ** tp) { for (;;) { trigger_type *ttype; char zText[128]; - READ_TOK(store, zText, sizeof(zText)); + READ_TOK(data->store, zText, sizeof(zText)); if (!strcmp(zText, "end")) break; ttype = tt_find(zText); assert(ttype || !"unknown trigger-type"); *tp = t_new(ttype); if (ttype->read) { - int i = ttype->read(*tp, store); + int i = ttype->read(*tp, data); switch (i) { case AT_READ_OK: tp = &(*tp)->next; break; case AT_READ_FAIL: t_free(*tp); + free(*tp); *tp = NULL; break; default: @@ -106,6 +108,7 @@ int handle_triggers(trigger ** triggers, void *param) if (t->type->handle(t, param) != 0) { *tp = t->next; t_free(t); + free(t); } else tp = &t->next; @@ -143,14 +146,15 @@ write_handler(const attrib * a, const void *owner, struct storage *store) write_triggers(store, hi->triggers); } -static int read_handler(attrib * a, void *owner, struct storage *store) +static int read_handler(attrib * a, void *owner, gamedata *data) { + struct storage *store = data->store; char zText[128]; handler_info *hi = (handler_info *)a->data.v; READ_TOK(store, zText, sizeof(zText)); hi->event = _strdup(zText); - read_triggers(store, &hi->triggers); + read_triggers(data, &hi->triggers); if (hi->triggers != NULL) { return AT_READ_OK; } @@ -258,6 +262,7 @@ const trigger_type * tt) if (t->type == tt) { *tp = t->next; t_free(t); + free(t); } else tp = &t->next; diff --git a/src/util/event.h b/src/util/event.h index 2fb5f4806..ad4ca373d 100644 --- a/src/util/event.h +++ b/src/util/event.h @@ -27,6 +27,7 @@ extern "C" { struct attrib; struct trigger; struct storage; + struct gamedata; typedef struct trigger_type { const char *name; @@ -34,7 +35,7 @@ extern "C" { void(*finalize) (struct trigger *); int(*handle) (struct trigger *, void *); void(*write) (const struct trigger *, struct storage * store); - int(*read) (struct trigger *, struct storage * store); + int(*read) (struct trigger *, struct gamedata * store); struct trigger_type *next; } trigger_type; @@ -73,7 +74,7 @@ extern "C" { /* functions for making complex triggers: */ void free_triggers(trigger * triggers); /* release all these triggers */ void write_triggers(struct storage *store, const trigger * t); - int read_triggers(struct storage *store, trigger ** tp); + int read_triggers(struct gamedata *data, trigger ** tp); int handle_triggers(trigger ** triggers, void *data); extern struct attrib_type at_eventhandler; diff --git a/src/util/gamedata.c b/src/util/gamedata.c new file mode 100644 index 000000000..cd3fa01a7 --- /dev/null +++ b/src/util/gamedata.c @@ -0,0 +1,73 @@ +#include + +#include "gamedata.h" +#include "log.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +void gamedata_done(gamedata *data) { + binstore_done(data->store); +} + +void gamedata_init(gamedata *data, storage *store, int version) { + data->version = version; + data->store = store; + binstore_init(data->store, &data->strm); +} + +int gamedata_openfile(gamedata *data, const char *filename, const char *mode, int version) { + FILE *F = fopen(filename, mode); + if (F) { + int err = 0; + + if (strchr(mode, 'r')) { + size_t sz; + sz = fread(&version, 1, sizeof(int), F); + if (sz != sizeof(int)) { + err = ferror(F); + } + else { + err = fseek(F, sizeof(int), SEEK_CUR); + } + } + else if (strchr(mode, 'w')) { + int n = STREAM_VERSION; + fwrite(&version, sizeof(int), 1, F); + fwrite(&n, sizeof(int), 1, F); + } + if (err) { + log_error("could not open %s: %s", filename, strerror(errno)); + fclose(F); + } + else { + storage *store = malloc(sizeof(storage)); + fstream_init(&data->strm, F); + gamedata_init(data, store, version); + } + return err; + } + return errno; +} + +gamedata *gamedata_open(const char *filename, const char *mode, int version) { + gamedata *data = (gamedata *)calloc(1, sizeof(gamedata)); + if (gamedata_openfile(data, filename, mode, version) != 0) { + free(data); + return NULL; + } + return data; +} + +void gamedata_close(gamedata *data) { + gamedata_done(data); + fstream_done(&data->strm); + free(data->store); +} diff --git a/src/util/gamedata.h b/src/util/gamedata.h new file mode 100644 index 000000000..e75f7d681 --- /dev/null +++ b/src/util/gamedata.h @@ -0,0 +1,25 @@ +#pragma once + +#ifndef _GAMEDATA_H +#define _GAMEDATA_H + +#include + +struct storage; + +typedef struct gamedata { + struct storage *store; + stream strm; + int version; +} gamedata; + +void gamedata_init(gamedata *data, struct storage *store, int version); +void gamedata_done(gamedata *data); + +void gamedata_close(gamedata *data); +gamedata *gamedata_open(const char *filename, const char *mode, int version); +int gamedata_openfile(gamedata *data, const char *filename, const char *mode, int version); + +#define STREAM_VERSION 2 /* internal encoding of binary files */ + +#endif diff --git a/src/util/gamedata.test.c b/src/util/gamedata.test.c index b28066b97..c58a8ddd0 100644 --- a/src/util/gamedata.test.c +++ b/src/util/gamedata.test.c @@ -11,9 +11,11 @@ static void test_gamedata(CuTest * tc) data = gamedata_open("test.dat", "wb", 0); CuAssertPtrNotNull(tc, data); gamedata_close(data); + free(data); data = gamedata_open("test.dat", "rb", 0); CuAssertPtrNotNull(tc, data); gamedata_close(data); + free(data); CuAssertIntEquals(tc, 0, remove("test.dat")); } diff --git a/src/util/log.c b/src/util/log.c index 36e81a5df..d330a96db 100644 --- a/src/util/log.c +++ b/src/util/log.c @@ -22,41 +22,36 @@ without prior permission by the authors of Eressea. #include #include -/* TODO: set from external function */ -int log_flags = LOG_FLUSH | LOG_CPERROR | LOG_CPWARNING | LOG_CPDEBUG; - #ifdef STDIO_CP static int stdio_codepage = STDIO_CP; #else static int stdio_codepage = 0; #endif -typedef struct logger { +typedef struct log_t { void(*log)(void *data, int level, const char *module, const char *format, va_list args); void *data; int flags; - int id; - struct logger *next; -} logger; + struct log_t *next; +} log_t; -static logger *loggers; -static int log_id; +static log_t *loggers; -int log_create(int flags, void *data, log_fun call) { - logger *lgr = malloc(sizeof(logger)); +log_t *log_create(int flags, void *data, log_fun call) { + log_t *lgr = malloc(sizeof(log_t)); lgr->log = call; lgr->flags = flags; lgr->data = data; lgr->next = loggers; loggers = lgr; - return lgr->id = ++log_id; + return lgr; } -void log_destroy(int id) { - logger ** lp = &loggers; +void log_destroy(log_t *handle) { + log_t ** lp = &loggers; while (*lp) { - logger *lg = *lp; - if (lg->id==id) { + log_t *lg = *lp; + if (lg==handle) { *lp = lg->next; free(lg); break; @@ -136,7 +131,7 @@ static const char *log_prefix(int level) { return prefix; } -static int check_dupe(const char *format, int type) +static int check_dupe(const char *format, int level) { static int last_type; /* STATIC_XCALL: used across calls */ static char last_message[32]; /* STATIC_XCALL: used across calls */ @@ -146,14 +141,14 @@ static int check_dupe(const char *format, int type) return 1; } if (dupes) { - if (log_flags & LOG_CPERROR) { + if (level & LOG_CPERROR) { fprintf(stderr, "%s: last message repeated %d times\n", log_prefix(last_type), dupes + 1); } dupes = 0; } strlcpy(last_message, format, sizeof(last_message)); - last_type = type; + last_type = level; return 0; } @@ -188,14 +183,15 @@ static void log_stdio(void *data, int level, const char *module, const char *for if (format[len - 1] != '\n') { fputc('\n', out); } + fflush(out); } -void log_to_file(int flags, FILE *out) { - log_create(flags, out, log_stdio); +log_t *log_to_file(int flags, FILE *out) { + return log_create(flags, out, log_stdio); } static void log_write(int flags, const char *module, const char *format, va_list args) { - logger *lg; + log_t *lg; for (lg = loggers; lg; lg = lg->next) { int level = flags & LOG_LEVELS; if (lg->flags & level) { @@ -268,7 +264,7 @@ static FILE *logfile; void log_close(void) { while (loggers) { - logger *lgr = loggers; + log_t *lgr = loggers; loggers = lgr->next; free(lgr); } @@ -281,7 +277,7 @@ void log_close(void) logfile = 0; } -void log_open(const char *filename) +log_t *log_open(const char *filename, int log_flags) { log_rotate(filename, LOG_MAXBACKUPS); logfile = fopen(filename, "a"); @@ -290,6 +286,14 @@ void log_open(const char *filename) time_t ltime; time(<ime); fprintf(logfile, "===\n=== Logfile started at %s===\n", ctime(<ime)); - log_create(log_flags, logfile, log_stdio); + return log_create(log_flags, logfile, log_stdio); } + return NULL; +} + +int log_level(log_t * log, int flags) +{ + int old = log->flags; + log->flags = flags; + return old; } diff --git a/src/util/log.h b/src/util/log.h index d19c4cf9b..06d150751 100644 --- a/src/util/log.h +++ b/src/util/log.h @@ -17,10 +17,17 @@ extern "C" { #include #include - extern void log_open(const char *filename); - extern void log_close(void); + struct log_t; + + typedef void(*log_fun)(void *data, int level, const char *module, const char *format, va_list args); + + struct log_t * log_open(const char *filename, int flags); + struct log_t * log_create(int flags, void *data, log_fun call); + void log_destroy(struct log_t *handle); + struct log_t * log_to_file(int flags, FILE *out); + int log_level(struct log_t *log, int flags); + void log_close(void); - /* use macros above instead of these: */ extern void log_fatal(const char *format, ...); extern void log_error(const char *format, ...); extern void log_warning(const char *format, ...); @@ -36,13 +43,7 @@ extern "C" { #define LOG_FLUSH 0x10 #define LOG_BRIEF 0x20 - typedef void(*log_fun)(void *data, int level, const char *module, const char *format, va_list args); - int log_create(int flags, void *data, log_fun call); - void log_destroy(int id); - void log_to_file(int flags, FILE *out); - - extern int log_flags; extern int log_stderr; #ifdef __cplusplus } diff --git a/src/util/log.test.c b/src/util/log.test.c index 6c2a89846..95cbefa96 100644 --- a/src/util/log.test.c +++ b/src/util/log.test.c @@ -19,14 +19,14 @@ static void test_logging(CuTest * tc) { char str1[32]; char str2[32]; - int id1 = log_create(LOG_CPWARNING, str1, log_string); - int id2 = log_create(LOG_CPWARNING, str2, log_string); + struct log_t * id1 = log_create(LOG_CPWARNING, str1, log_string); + struct log_t * id2 = log_create(LOG_CPWARNING, str2, log_string); CuAssertTrue(tc, id1!=id2); log_warning("Hello %s", "World"); - CuAssertStrEquals(tc, str1, "World"); - CuAssertStrEquals(tc, str2, "World"); log_destroy(id1); log_destroy(id2); + CuAssertStrEquals(tc, "World", str1); + CuAssertStrEquals(tc, "World", str2); } CuSuite *get_log_suite(void) diff --git a/src/util/parser.c b/src/util/parser.c index 63b293e82..741fd573f 100644 --- a/src/util/parser.c +++ b/src/util/parser.c @@ -192,7 +192,7 @@ char *parse_token(const char **str, char *lbuf, size_t buflen) copy = true; } if (copy) { - if (cursor - buflen < lbuf - 1) { + if (cursor - buflen < lbuf - len) { memcpy(cursor, ctoken, len); cursor += len; } diff --git a/src/util/parser.test.c b/src/util/parser.test.c index 95ef70a17..da0d8dacb 100644 --- a/src/util/parser.test.c +++ b/src/util/parser.test.c @@ -1,8 +1,58 @@ #include #include "parser.h" +#include #include +static void test_parse_token(CuTest *tc) { + char lbuf[8]; + const char *tok; + const char *str, *orig; + + orig = str = "SHORT TOKEN"; + tok = parse_token(&str, lbuf, sizeof(lbuf)); + CuAssertPtrEquals(tc, (void *)(orig+5), (void *)str); + CuAssertStrEquals(tc, "SHORT", tok); + tok = parse_token(&str, lbuf, sizeof(lbuf)); + CuAssertPtrEquals(tc, (void *)(orig + strlen(orig)), (void *)str); + CuAssertStrEquals(tc, "TOKEN", tok); + tok = parse_token(&str, lbuf, sizeof(lbuf)); + CuAssertPtrEquals(tc, NULL, (void *)tok); +} + +static void test_parse_token_limit(CuTest *tc) { + char lbuf[8]; + const char *tok; + const char *str, *orig; + + orig = str = "LONG_TOKEN"; + tok = parse_token(&str, lbuf, sizeof(lbuf)); + CuAssertPtrEquals(tc, (void *)(orig + strlen(orig)), (void *)str); + CuAssertStrEquals(tc, tok, "LONG_TO"); + tok = parse_token(&str, lbuf, sizeof(lbuf)); + CuAssertPtrEquals(tc, NULL, (void *)tok); +} + +static void test_parse_token_limit_utf8(CuTest *tc) { + char lbuf[8]; + const char *tok; + const char *orig = "a\xc3\xa4\xc3\xb6\xc3\xbc\xc3\x9f"; /* auml ouml uuml szlig, 8 bytes long */ + const char *str = orig+1; + + tok = parse_token(&str, lbuf, sizeof(lbuf)); + CuAssertPtrEquals(tc, (void *)(orig + strlen(orig)), (void *)str); + CuAssertStrEquals(tc, tok, "\xc3\xa4\xc3\xb6\xc3\xbc"); // just three letters fit, 6 bytes long + tok = parse_token(&str, lbuf, sizeof(lbuf)); + CuAssertPtrEquals(tc, NULL, (void *)tok); + + str = orig; // now with an extra byte in the front, maxing out lbuf exactly + tok = parse_token(&str, lbuf, sizeof(lbuf)); + CuAssertPtrEquals(tc, (void *)(orig + strlen(orig)), (void *)str); + CuAssertStrEquals(tc, tok, "a\xc3\xa4\xc3\xb6\xc3\xbc"); + tok = parse_token(&str, lbuf, sizeof(lbuf)); + CuAssertPtrEquals(tc, NULL, (void *)tok); +} + static void test_gettoken(CuTest *tc) { char token[128]; init_tokens_str("HELP ONE TWO THREE"); @@ -64,6 +114,9 @@ CuSuite *get_parser_suite(void) CuSuite *suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_atoip); SUITE_ADD_TEST(suite, test_skip_token); + SUITE_ADD_TEST(suite, test_parse_token); + SUITE_ADD_TEST(suite, test_parse_token_limit); + SUITE_ADD_TEST(suite, test_parse_token_limit_utf8); SUITE_ADD_TEST(suite, test_gettoken); SUITE_ADD_TEST(suite, test_gettoken_short); SUITE_ADD_TEST(suite, test_getintegers); diff --git a/src/util/password.c b/src/util/password.c index 3434fb407..ac482d1f4 100644 --- a/src/util/password.c +++ b/src/util/password.c @@ -1,132 +1,18 @@ #include #include "password.h" -#include -#include -#include -#include - #include #include #include -#include - -#define MAXSALTLEN 32 // maximum length in characters of any salt -#define SALTLEN 8 // length of salts we generate - -#define b64_from_24bit(B2, B1, B0, N) \ - do { \ - unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \ - int n = (N); \ - while (n-- > 0 && buflen > 0) \ - { \ - *cp++ = itoa64[w & 0x3f]; \ - --buflen; \ - w >>= 6; \ - } \ - } while (0) - - -char *password_gensalt(char *salt, size_t salt_len) { - size_t buflen = salt_len-1; - char *cp = salt; - while (buflen) { - unsigned long ul = genrand_int32() & (unsigned long)time(0); - b64_from_24bit((char)(ul & 0xFF), (char)((ul >> 8) & 0xff), (char)((ul >> 16) & 0xFF), 4); - } - salt[salt_len-1] = 0; - return salt; -} bool password_is_implemented(int algo) { - return algo == PASSWORD_PLAINTEXT;// || algo == PASSWORD_BCRYPT || algo == PASSWORD_NOCRYPT || algo == PASSWORD_MD5 || algo == PASSWORD_APACHE_MD5; -} - -static const char * password_hash_i(const char * passwd, const char *input, int algo, char *result, size_t len) { - if (algo == PASSWORD_BCRYPT) { - char salt[MAXSALTLEN]; - char setting[40]; - if (!input) { - input = password_gensalt(salt, MAXSALTLEN); - } - if (_crypt_gensalt_blowfish_rn("$2y$", 5, input, strlen(input), setting, sizeof(setting)) == NULL) { - return NULL; - } - if (_crypt_blowfish_rn(passwd, setting, result, len) == NULL) { - return NULL; - } - return result; - } - else if (algo == PASSWORD_PLAINTEXT) { - _snprintf(result, len, "%s", passwd); - return result; - } - else if (algo == PASSWORD_NOCRYPT) { - _snprintf(result, len, "$0$%s", passwd); - return result; - } - else if (password_is_implemented(algo)) { - char salt[MAXSALTLEN]; - assert(passwd); - if (input) { - const char * dol = strchr(input, '$'); - size_t salt_len; - if (dol) { - assert(dol > input && dol[0] == '$'); - salt_len = dol - input; - } - else { - salt_len = strlen(input); - } - assert(salt_len < MAXSALTLEN); - memcpy(salt, input, salt_len); - salt[salt_len] = 0; - } else { - input = password_gensalt(salt, sizeof(salt)); - } - if (algo == PASSWORD_MD5) { - return md5_crypt_r(passwd, input, result, len); - } - else if (algo == PASSWORD_APACHE_MD5) { - apr_md5_encode(passwd, input, result, len); - return result; - } - } - return NULL; + return algo == PASSWORD_PLAINTEXT; } const char * password_encode(const char * passwd, int algo) { - static char result[64]; // TODO: static result buffers are bad mojo! - if (algo < 0) algo = PASSWORD_DEFAULT; - return password_hash_i(passwd, 0, algo, result, sizeof(result)); + return passwd; } int password_verify(const char * pwhash, const char * passwd) { - char hash[64]; - int algo = PASSWORD_PLAINTEXT; - char *pos; - const char *result; - assert(passwd); - assert(pwhash); - if (pwhash[0] == '$') { - algo = pwhash[1]; - } - if (!password_is_implemented(algo)) { - return VERIFY_UNKNOWN; - } - if (algo == PASSWORD_PLAINTEXT) { - return (strcmp(passwd, pwhash) == 0) ? VERIFY_OK : VERIFY_FAIL; - } else if (algo == PASSWORD_BCRYPT) { - char sample[200]; - _crypt_blowfish_rn(passwd, pwhash, sample, sizeof(sample)); - return (strcmp(sample, pwhash) == 0) ? VERIFY_OK : VERIFY_FAIL; - } - pos = strchr(pwhash+2, '$'); - assert(pos && pos[0] == '$'); - pos = strchr(pos, '$')+1; - result = password_hash_i(passwd, pos, algo, hash, sizeof(hash)); - if (strcmp(pwhash, result) == 0) { - return VERIFY_OK; - } - return VERIFY_FAIL; + return (strcmp(passwd, pwhash) == 0) ? VERIFY_OK : VERIFY_FAIL; } diff --git a/src/util/password.h b/src/util/password.h index 90912cd9d..e1d49fb6d 100644 --- a/src/util/password.h +++ b/src/util/password.h @@ -1,12 +1,6 @@ #pragma once #define PASSWORD_PLAINTEXT 0 -#define PASSWORD_NOCRYPT '0' -#define PASSWORD_MD5 '1' -#define PASSWORD_BCRYPT '2' // not implemented -#define PASSWORD_APACHE_MD5 'a' -#define PASSWORD_SHA256 '5' // not implemented -#define PASSWORD_SHA512 '6' // not implemented #define PASSWORD_DEFAULT PASSWORD_PLAINTEXT #define VERIFY_OK 0 // password matches hash diff --git a/src/util/password.test.c b/src/util/password.test.c index 4c038642c..8efe5b107 100644 --- a/src/util/password.test.c +++ b/src/util/password.test.c @@ -5,27 +5,7 @@ static void test_passwords(CuTest *tc) { const char *hash, *expect; - - expect = "$apr1$FqQLkl8g$.icQqaDJpim4BVy.Ho5660"; - if (password_is_implemented(PASSWORD_APACHE_MD5)) { - CuAssertIntEquals(tc, VERIFY_OK, password_verify(expect, "Hodor")); - hash = password_encode("Hodor", PASSWORD_APACHE_MD5); - CuAssertPtrNotNull(tc, hash); - CuAssertIntEquals(tc, 0, strncmp(hash, expect, 6)); - } else { - CuAssertIntEquals(tc, VERIFY_UNKNOWN, password_verify(expect, "Hodor")); - } - - expect = "$1$ZouUn04i$yNnT1Oy8azJ5V.UM9ppP5/"; - if (password_is_implemented(PASSWORD_MD5)) { - CuAssertIntEquals(tc, VERIFY_OK, password_verify(expect, "jollygood")); - hash = password_encode("jollygood", PASSWORD_MD5); - CuAssertPtrNotNull(tc, hash); - CuAssertIntEquals(tc, 0, strncmp(hash, expect, 3)); - } else { - CuAssertIntEquals(tc, VERIFY_UNKNOWN, password_verify(expect, "jollygood")); - } - + expect = "password"; if (password_is_implemented(PASSWORD_PLAINTEXT)) { hash = password_encode("password", PASSWORD_PLAINTEXT); @@ -36,29 +16,6 @@ static void test_passwords(CuTest *tc) { } else { CuAssertIntEquals(tc, VERIFY_UNKNOWN, password_verify(expect, "password")); } - - expect = "$0$password"; - if (password_is_implemented(PASSWORD_NOCRYPT)) { - hash = password_encode("password", PASSWORD_NOCRYPT); - CuAssertPtrNotNull(tc, hash); - CuAssertStrEquals(tc, hash, expect); - CuAssertIntEquals(tc, VERIFY_OK, password_verify(expect, "password")); - CuAssertIntEquals(tc, VERIFY_FAIL, password_verify(expect, "arseword")); - } else { - CuAssertIntEquals(tc, VERIFY_UNKNOWN, password_verify(expect, "password")); - } - - expect = "$2y$05$RJ8qAhu.foXyJLdc2eHTLOaK4MDYn3/v4HtOVCq0Plv2yxcrEB7Wm"; - if (password_is_implemented(PASSWORD_BCRYPT)) { - CuAssertIntEquals(tc, VERIFY_OK, password_verify(expect, "Hodor")); - hash = password_encode("Hodor", PASSWORD_BCRYPT); - CuAssertPtrNotNull(tc, hash); - CuAssertIntEquals(tc, 0, strncmp(hash, expect, 7)); - } else { - CuAssertIntEquals(tc, VERIFY_UNKNOWN, password_verify(expect, "Hodor")); - } - - CuAssertIntEquals(tc, VERIFY_UNKNOWN, password_verify("$9$saltyfish$password", "password")); } CuSuite *get_password_suite(void) { diff --git a/src/util/resolve.c b/src/util/resolve.c index e8dd0c17a..63ef5d933 100644 --- a/src/util/resolve.c +++ b/src/util/resolve.c @@ -45,10 +45,10 @@ variant read_int(struct storage *store) } int -read_reference(void *address, storage * store, read_fun reader, -resolve_fun resolver) +read_reference(void *address, struct gamedata * data, read_fun reader, + resolve_fun resolver) { - variant var = reader(store); + variant var = reader(data); int result = resolver(var, address); if (result != 0) { ur_add(var, address, resolver); diff --git a/src/util/resolve.h b/src/util/resolve.h index 8d24cc359..076b20508 100644 --- a/src/util/resolve.h +++ b/src/util/resolve.h @@ -21,14 +21,15 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "variant.h" struct storage; +struct gamedata; #ifdef __cplusplus extern "C" { #endif typedef int(*resolve_fun) (variant data, void *address); - typedef variant(*read_fun) (struct storage * store); - extern int read_reference(void *address, struct storage *store, + typedef variant(*read_fun) (struct gamedata * data); + extern int read_reference(void *address, struct gamedata *data, read_fun reader, resolve_fun resolver); extern void ur_add(variant data, void *address, resolve_fun fun); diff --git a/src/util/umlaut.c b/src/util/umlaut.c index 872ad8ffd..abbd59372 100644 --- a/src/util/umlaut.c +++ b/src/util/umlaut.c @@ -171,7 +171,6 @@ void addtoken(void ** root, const char *str, variant id) ref = (tref *)malloc(sizeof(tref)); ref->ucs = ucs; - ref->node = 0; ref->node = node; ref->nexthash = tk->next[index]; tk->next[index] = ref; diff --git a/src/util/unicode.c b/src/util/unicode.c index 4be39a551..94b6e3d5a 100644 --- a/src/util/unicode.c +++ b/src/util/unicode.c @@ -518,10 +518,22 @@ size_t * length) return 0; } +/** Convert a UTF-8 encoded character to ASCII, with '?' replacements. */ +int unicode_utf8_to_ascii(char *cp_character, const utf8_t * utf8_string, + size_t *length) +{ + int result = unicode_utf8_to_cp437(cp_character, utf8_string, length); + if (result == 0) { + if (*length > 1) { + *cp_character = '?'; + } + } + return result; +} + /** Convert a UTF-8 encoded character to CP1252. */ -int -unicode_utf8_to_cp1252(char *cp_character, const utf8_t * utf8_string, -size_t * length) +int unicode_utf8_to_cp1252(char *cp_character, const utf8_t * utf8_string, + size_t * length) { ucs4_t ucs4_character; int result; diff --git a/src/util/unicode.h b/src/util/unicode.h index 3408ef948..b061cd6fb 100644 --- a/src/util/unicode.h +++ b/src/util/unicode.h @@ -28,18 +28,20 @@ extern "C" { typedef unsigned long ucs4_t; typedef char utf8_t; - extern int unicode_utf8_to_cp437(char *result, const utf8_t * utf8_string, + int unicode_utf8_to_cp437(char *result, const utf8_t * utf8_string, size_t * length); - extern int unicode_utf8_to_cp1252(char *result, const utf8_t * utf8_string, + int unicode_utf8_to_cp1252(char *result, const utf8_t * utf8_string, size_t * length); - extern int unicode_utf8_to_ucs4(ucs4_t * result, const utf8_t * utf8_string, + int unicode_utf8_to_ucs4(ucs4_t * result, const utf8_t * utf8_string, size_t * length); - extern int unicode_ucs4_to_utf8(utf8_t * result, size_t * size, + int unicode_ucs4_to_utf8(utf8_t * result, size_t * size, ucs4_t ucs4_character); - extern int unicode_utf8_strcasecmp(const utf8_t * a, const utf8_t * b); - extern int unicode_latin1_to_utf8(utf8_t * out, size_t * outlen, + int unicode_utf8_to_ascii(char *cp_character, const utf8_t * utf8_string, + size_t *length); + int unicode_utf8_strcasecmp(const utf8_t * a, const utf8_t * b); + int unicode_latin1_to_utf8(utf8_t * out, size_t * outlen, const char *in, size_t * inlen); - extern int unicode_utf8_tolower(utf8_t * out, size_t outlen, + int unicode_utf8_tolower(utf8_t * out, size_t outlen, const utf8_t * in); #ifdef __cplusplus diff --git a/src/vortex.c b/src/vortex.c index 1d558bb49..8aac12aec 100644 --- a/src/vortex.c +++ b/src/vortex.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -74,8 +75,9 @@ static int a_agedirection(attrib * a, void *owner) return (d->duration > 0) ? AT_AGE_KEEP : AT_AGE_REMOVE; } -static int a_readdirection(attrib * a, void *owner, struct storage *store) +static int a_readdirection(attrib * a, void *owner, struct gamedata *data) { + struct storage *store = data->store; spec_direction *d = (spec_direction *)(a->data.v); char lbuf[32]; @@ -149,7 +151,7 @@ attrib *create_special_direction(region * r, region * rt, int duration, spec_direction *special_direction(const region * from, const region * to) { - const attrib *a = a_findc(from->attribs, &at_direction); + const attrib *a = a_find(from->attribs, &at_direction); while (a != NULL && a->type == &at_direction) { spec_direction *sd = (spec_direction *)a->data.v; diff --git a/src/vortex.test.c b/src/vortex.test.c index ba3c91354..78a04535f 100644 --- a/src/vortex.test.c +++ b/src/vortex.test.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -30,6 +31,7 @@ static void test_move_to_vortex(CuTest *tc) { r2 = test_create_region(5, 0, t_plain); CuAssertPtrNotNull(tc, create_special_direction(r1, r2, 10, "", "vortex", true)); u = test_create_unit(test_create_faction(rc_get_or_create("hodor")), r1); + u->faction->locale = lang; CuAssertIntEquals(tc, E_MOVE_NOREGION, movewhere(u, "barf", r1, &r)); CuAssertIntEquals(tc, E_MOVE_OK, movewhere(u, "wirbel", r1, &r)); CuAssertPtrEquals(tc, r2, r); diff --git a/src/wormhole.c b/src/wormhole.c index 684cb8f4d..916c0509d 100644 --- a/src/wormhole.c +++ b/src/wormhole.c @@ -27,6 +27,7 @@ /* util includes */ #include +#include #include #include #include @@ -109,17 +110,18 @@ static int resolve_exit(variant id, void *address) return -1; } -static int wormhole_read(struct attrib *a, void *owner, struct storage *store) +static int wormhole_read(struct attrib *a, void *owner, struct gamedata *data) { - resolve_fun resolver = (global.data_version < UIDHASH_VERSION) + storage *store = data->store; + resolve_fun resolver = (data->version < UIDHASH_VERSION) ? resolve_exit : resolve_region_id; - read_fun reader = (global.data_version < UIDHASH_VERSION) + read_fun reader = (data->version < UIDHASH_VERSION) ? read_building_reference : read_region_reference; - if (global.data_version < ATTRIBOWNER_VERSION) { + if (data->version < ATTRIBOWNER_VERSION) { READ_INT(store, NULL); } - if (read_reference(&a->data.v, store, reader, resolver) == 0) { + if (read_reference(&a->data.v, data, reader, resolver) == 0) { if (!a->data.v) { return AT_READ_FAIL; } @@ -134,6 +136,7 @@ static attrib_type at_wormhole = { wormhole_age, wormhole_write, wormhole_read, + NULL, ATF_UNIQUE }; @@ -146,8 +149,7 @@ make_wormhole(const building_type * bt_wormhole, region * r1, region * r2) attrib *a2 = a_add(&b2->attribs, a_new(&at_wormhole)); a1->data.v = b2->region; a2->data.v = b1->region; - b1->size = bt_wormhole->maxsize; - b2->size = bt_wormhole->maxsize; + b1->size = b2->size = bt_wormhole->maxcapacity * bt_wormhole->capacity; ADDMSG(&r1->msgs, msg_message("wormhole_appear", "region", r1)); ADDMSG(&r2->msgs, msg_message("wormhole_appear", "region", r2)); } diff --git a/tests/drmemory.bat b/tests/drmemory.bat new file mode 100644 index 000000000..1d07081aa --- /dev/null +++ b/tests/drmemory.bat @@ -0,0 +1,7 @@ +cd c:\users\enno\documents\eressea\git\tests + +"C:\Program Files (x86)\Dr. Memory\bin64\drmemory.exe" ..\build-vs14\eressea\Debug\eressea.exe -t184 test-turn.lua + +del reports +del datum htpasswd parteien parteien.full passwd score turn +pause diff --git a/tests/run-turn.sh b/tests/run-turn.sh index e6118329c..d47ac81a2 100755 --- a/tests/run-turn.sh +++ b/tests/run-turn.sh @@ -1,4 +1,5 @@ NEWFILES="data/185.dat datum parteien parteien.full passwd htpasswd score turn" + cleanup () { rm -rf reports $NEWFILES } @@ -19,6 +20,7 @@ expr=$2 expect=$3 count=`grep -cE $expr $file` [ $count -eq $expect ] || quit 1 "expected $expect counts of $expr in $file, got $count" +echo "PASS: $expr is $expect" } ROOT=`pwd` @@ -34,10 +36,11 @@ VALGRIND=`which valgrind` SERVER=../Debug/eressea/eressea if [ -n "$VALGRIND" ]; then SUPP=../share/ubuntu-12_04.supp -SERVER="$VALGRIND --suppressions=$SUPP --error-exitcode=1 --leak-check=no $SERVER" +SERVER="$VALGRIND --track-origins=yes --gen-suppressions=all --suppressions=$SUPP --error-exitcode=1 --leak-check=no $SERVER" fi echo "running $SERVER" $SERVER -t 184 test-turn.lua +echo "integration tests" [ -d reports ] || quit 4 "no reports directory created" CRFILE=185-zvto.cr for file in $NEWFILES reports/$CRFILE ; do