diff --git a/CHANGELOG.md b/CHANGELOG.md index 16ec084e0..d54dcb31b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,30 @@ -# Geplante Aenderungen in zukuenftigen Eressea-Versionen -Als Anhaltspunkt fuer die Empfaenger der woechentlichen Testauswertungnen -will ich versuchen, die Liste meiner Aenderungen seit dem letzten Release -zu dokumentieren. +# 3.26 -## Version 3.12.0 + - Akademien, Traenke und Verzauberungen wirken auch bei LERNE AUTO + - Das lernen in einer Akademie erhoeht die Lernkosten. Koennen diese + nicht bezahlt werden, wird ohne deren Bonus gelernt. + - Lehrer muessen nicht mehr in der Akademie stehen, damit ihre Schueler + den Bonus bekommen + - Rohstoffe koennen jetzt bereits gesehen werden, wenn eine Einheit nur + die Haelfte des zum Abbau noetigen Talentes hat (statt bisher + Talent-1) + - Mauern der Ewigkeit und Störe Astrale Integrität brauchen keine + Stufenangabe, ihre Kosten sind nicht variabel [2651] + +# 3.25 + + - Ab sofort ist es nicht mehr erlaubt, Befehle mit weniger als 3 + Zeichen abzukürzen. + - Leuchttürme entdecken Seeschlangen und Drachen auf dem Ozean [2688] + - Magieresistenz von Insekten und Goblins repariert [2685] + - Getarnte Einheiten können wieder Eisen abbauen [2679] + - Gestaltwandlung kann nur einmal auf die selbe Einheit wirken [2680] + - Handel benötigt eine Burg mit Mindestgröße 2 [2678] + - Geschützte Leerzeichen in Befehlen werden ignoriert [2670] + +# 3.12 -- [other] optimierte Berechnung der Sichtbarkeit von Leuchttuermen - [bug] Einheitenlimit bei GIB PERSON beachten - [bug] Einheitenlimit bei ALLIANCE JOIN beachten - [rule] Einheiten- und Personenzahl im Report beinhaltet *alle* Einheiten der Partei. diff --git a/conf/e2/catalog.xml b/conf/e2/catalog.xml deleted file mode 100644 index d0c7333aa..000000000 --- a/conf/e2/catalog.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0"?> -<!DOCTYPE catalog - PUBLIC "-//OASIS/DTD Entity Resolution XML Catalog V1.0//EN" - "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd"> - -<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog"> - <rewriteURI - uriStartString="config://core/" - rewritePrefix="../../res/core/" /> - <rewriteURI - uriStartString="config://game/" - rewritePrefix="../../res/eressea/" /> - <rewriteURI - uriStartString="config://default/" - rewritePrefix="../../res/" /> -</catalog> diff --git a/conf/e2/config.json b/conf/e2/config.json index 114fffe52..fdb616816 100644 --- a/conf/e2/config.json +++ b/conf/e2/config.json @@ -1,63 +1,64 @@ { - "include": [ - "config://conf/keywords.json", - "config://conf/calendar.json", - "config://conf/prefixes.json", - "config://conf/e2/locales.json", - "config://conf/e2/terrains.json", - "config://conf/e2/items.json", - "config://res/core/ships.xml", - "config://res/core/common/buildings.xml", - "config://res/eressea/buildings.xml", - "config://res/buildings/castle.xml", - "config://res/eressea/races.xml", - "config://res/eressea/artrewards.xml", - "config://res/eressea/spells.xml", - "config://res/eressea/spellbooks/gray.xml", - "config://res/eressea/spellbooks/gwyrrd.xml", - "config://res/eressea/spellbooks/draig.xml", - "config://res/eressea/spellbooks/illaun.xml", - "config://res/eressea/spellbooks/cerddor.xml", - "config://res/eressea/spellbooks/tybied.xml" - ], - "disabled": [ - "jsreport" - ], - "settings": { - "game.name" : "Eressea", - "game.mailcmd" : "ERESSEA", - "game.id" : 2, - "orders.default": "work", - "NewbieImmunity": 8, - "modules.market": false, - "modules.astralspace": true, - "modules.wormhole": true, - "modules.iceberg": true, - "modules.volcano": true, - "monsters.spawn.chance": 50, - "entertain.base": 0, - "entertain.perlevel": 20, - "taxing.perlevel": 20, - "nmr.timeout": 5, - "nmr.removenewbie": false, - "GiveRestriction": 3, - "hunger.long": false, - "hunger.damage": "1d8+6", - "init_spells": 0, - "game.era": 2, - "game.start": 184, - "rules.reserve.twophase": true, - "rules.give.max_men": -1, - "rules.check_overload": false, - "rules.limit.faction": 2500, - "rules.maxskills.magic": 5, - "rules.guard.base_stop_prob": 0.30, - "rules.guard.skill_stop_prob": 0.05, - "rules.guard.amulet_stop_prob": 0.10, - "rules.guard.guard_number_stop_prob": 0.001, - "rules.guard.castle_stop_prob": 0.05, - "rules.guard.region_type_stop_prob": 0.05, - "rules.economy.repopulate_maximum": 500, - "rules.lighthouse.unit_capacity": true - } + "settings": { + "game.name": "Eressea", + "game.mailcmd": "ERESSEA", + "game.id": 2, + "orders.default": "work", + "NewbieImmunity": 8, + "modules.market": false, + "modules.astralspace": true, + "modules.wormhole": true, + "modules.iceberg": true, + "modules.volcano": true, + "monsters.spawn.chance": 50, + "entertain.base": 0, + "entertain.perlevel": 20, + "taxing.perlevel": 20, + "nmr.timeout": 5, + "nmr.removenewbie": false, + "GiveRestriction": 3, + "hunger.long": false, + "hunger.damage": "1d8+6", + "init_spells": 0, + "game.era": 2, + "game.start": 184, + "rules.reserve.twophase": true, + "rules.give.max_men": -1, + "rules.check_overload": false, + "rules.limit.faction": 2500, + "rules.maxskills.magic": 5, + "rules.guard.base_stop_prob": 0.30, + "rules.guard.skill_stop_prob": 0.05, + "rules.guard.amulet_stop_prob": 0.10, + "rules.guard.guard_number_stop_prob": 0.001, + "rules.guard.castle_stop_prob": 0.05, + "rules.guard.region_type_stop_prob": 0.05, + "rules.economy.repopulate_maximum": 500, + "rules.lighthouse.unit_capacity": true, + "resource.visibility.rule": 0 + }, + "disabled": [ + "jsreport" + ], + "include": [ + "config://conf/keywords.json", + "config://conf/calendar.json", + "config://conf/prefixes.json", + "config://conf/e2/locales.json", + "config://conf/e2/terrains.json", + "config://conf/e2/items.json", + "config://res/core/ships.xml", + "config://res/core/common/buildings.xml", + "config://res/eressea/buildings.xml", + "config://res/buildings/castle.xml", + "config://res/eressea/races.xml", + "config://res/eressea/artrewards.xml", + "config://res/eressea/spells.xml", + "config://res/eressea/spellbooks/gray.xml", + "config://res/eressea/spellbooks/gwyrrd.xml", + "config://res/eressea/spellbooks/draig.xml", + "config://res/eressea/spellbooks/illaun.xml", + "config://res/eressea/spellbooks/cerddor.xml", + "config://res/eressea/spellbooks/tybied.xml" + ] } diff --git a/conf/e3/catalog.xml b/conf/e3/catalog.xml deleted file mode 100644 index 9987e72c8..000000000 --- a/conf/e3/catalog.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0"?> -<!DOCTYPE catalog - PUBLIC "-//OASIS/DTD Entity Resolution XML Catalog V1.0//EN" - "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd"> - -<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog"> - <rewriteURI - uriStartString="config://core/" - rewritePrefix="../../res/core/" /> - <rewriteURI - uriStartString="config://game/" - rewritePrefix="../../res/e3a/" /> - <rewriteURI - uriStartString="config://default/" - rewritePrefix="../../res/" /> -</catalog> diff --git a/conf/e3/config.json b/conf/e3/config.json index dcc3e9f53..c7c390372 100644 --- a/conf/e3/config.json +++ b/conf/e3/config.json @@ -67,7 +67,6 @@ "init_spells": 0, "recruit.allow_merge": true, "study.expensivemigrants": true, - "study.speedup": 2, "game.era": 3, "game.start": 1, "rules.reserve.twophase": true, diff --git a/conf/ex/config.json b/conf/ex/config.json new file mode 100644 index 000000000..58d460f85 --- /dev/null +++ b/conf/ex/config.json @@ -0,0 +1,68 @@ +{ + "include": [ + "config://conf/keywords.json", + "config://conf/calendar.json", + "config://conf/prefixes.json", + "config://conf/e2/locales.json", + "config://conf/e2/terrains.json", + "config://conf/e2/items.json", + "config://res/core/ships.xml", + "config://res/core/common/buildings.xml", + "config://res/eressea/buildings.xml", + "config://res/buildings/castle.xml", + "config://res/eressea/races.xml", + "config://res/eressea/artrewards.xml", + "config://res/eressea/spells.xml", + "config://res/eressea/spellbooks/gray.xml", + "config://res/eressea/spellbooks/gwyrrd.xml", + "config://res/eressea/spellbooks/draig.xml", + "config://res/eressea/spellbooks/illaun.xml", + "config://res/eressea/spellbooks/cerddor.xml", + "config://res/eressea/spellbooks/tybied.xml" + ], + "disabled": [ + "destroy", + "steal", + "number", + "jsreport" + ], + "settings": { + "game.name" : "Eressea", + "game.mailcmd" : "ERESSEA", + "game.id" : 2, + "orders.default": "work", + "NewbieImmunity": 8, + "modules.market": false, + "modules.astralspace": true, + "modules.wormhole": true, + "modules.iceberg": true, + "modules.volcano": true, + "monsters.spawn.chance": 50, + "entertain.base": 0, + "entertain.perlevel": 20, + "taxing.perlevel": 20, + "nmr.timeout": 5, + "nmr.removenewbie": false, + "GiveRestriction": 3, + "hunger.long": false, + "hunger.damage": "1d8+6", + "init_spells": 0, + "game.era": 2, + "game.start": 184, + "rules.reserve.twophase": true, + "rules.give.max_men": -1, + "rules.check_overload": false, + "rules.wage.function": 2, + "monsters.spawn.chance" : 0, + "rules.limit.faction": 2500, + "rules.maxskills.magic": 5, + "rules.guard.base_stop_prob": 0.30, + "rules.guard.skill_stop_prob": 0.05, + "rules.guard.amulet_stop_prob": 0.10, + "rules.guard.guard_number_stop_prob": 0.001, + "rules.guard.castle_stop_prob": 0.05, + "rules.guard.region_type_stop_prob": 0.05, + "rules.economy.repopulate_maximum": 500, + "rules.lighthouse.unit_capacity": true + } +} diff --git a/conf/ex/items.json b/conf/ex/items.json new file mode 100644 index 000000000..643248d32 --- /dev/null +++ b/conf/ex/items.json @@ -0,0 +1,50 @@ +{ + "include": [ + "config://res/core/spoils.xml", + "config://res/core/common/herbs.xml", + "config://res/core/common/items.xml", + "config://res/core/common/luxuries.xml", + "config://res/core/common/potions.xml", + "config://res/core/armor/chainmail.xml", + "config://res/core/armor/laenmail.xml", + "config://res/core/armor/laenshield.xml", + "config://res/core/armor/plate.xml", + "config://res/core/armor/rustychainmail.xml", + "config://res/core/armor/rustyshield.xml", + "config://res/core/armor/shield.xml", + "config://res/core/resources/cart.xml", + "config://res/core/resources/horse.xml", + "config://res/core/resources/hp.xml", + "config://res/core/resources/iron.xml", + "config://res/core/resources/laen.xml", + "config://res/core/resources/log.xml", + "config://res/core/resources/mallorn.xml", + "config://res/core/resources/mallornseed.xml", + "config://res/core/resources/seed.xml", + "config://res/core/resources/peasant.xml", + "config://res/core/resources/stone.xml", + "config://res/core/weapons/axe.xml", + "config://res/core/weapons/bow.xml", + "config://res/core/weapons/catapult.xml", + "config://res/core/weapons/crossbow.xml", + "config://res/core/weapons/firesword.xml", + "config://res/core/weapons/greatbow.xml", + "config://res/core/weapons/greatsword.xml", + "config://res/core/weapons/halberd.xml", + "config://res/core/weapons/laensword.xml", + "config://res/core/weapons/lance.xml", + "config://res/core/weapons/mallornbow.xml", + "config://res/core/weapons/mallorncrossbow.xml", + "config://res/core/weapons/mallornlance.xml", + "config://res/core/weapons/mallornspear.xml", + "config://res/core/weapons/runesword.xml", + "config://res/core/weapons/rustyaxe.xml", + "config://res/core/weapons/rustygreatsword.xml", + "config://res/core/weapons/rustyhalberd.xml", + "config://res/core/weapons/rustysword.xml", + "config://res/core/weapons/spear.xml", + "config://res/core/weapons/sword.xml", + "config://res/eressea/items.xml", + "config://res/adamantium.xml" + ] +} diff --git a/conf/ex/locales.json b/conf/ex/locales.json new file mode 100644 index 000000000..02967d134 --- /dev/null +++ b/conf/ex/locales.json @@ -0,0 +1,34 @@ +{ + "include": [ + "config://res/translations/strings.de.po", + "config://res/translations/strings-e2.de.po", + "config://res/translations/strings.en.po", + "config://res/translations/strings-e2.en.po", + "config://res/translations/messages.de.po", + "config://res/translations/messages.en.po", + "config://res/core/messages.xml" + ], + "aliases": { + "de": { + "spell::earthquake": [ + "Beschwöre einen Erdelementar", + "Beschwörung eines Erdelementares" + ], + "spell::goodwinds": [ + "Beschwörung eines Wasserelementares", + "Beschwöre einen Wasserelementar" + ], + "spell::stormwinds": [ + "Beschwöre einen Sturmelementar", + "Beschwörung eines Sturmelementares" + ], + "spell::summonfireelemental": [ + "Beschwöre einen Hitzeelementar", + "Beschwörung eines Hitzeelementares" + ] + }, + "en": { + "spell::migration": "Rit of Acceptance" + } + } +} diff --git a/conf/ex/readme.txt b/conf/ex/readme.txt new file mode 100644 index 000000000..c3e8801e9 --- /dev/null +++ b/conf/ex/readme.txt @@ -0,0 +1,8 @@ +# Änderungen gegenüber E2 + +- ARBEITE produziert nur genug Silber für den Unterhalt der Einheit +- NUMMER Befehl abgeschafft. +- Ebene und Hochland haben 20%, alle anderen Regionen 10% der Arbeitsplätze von E2. +- Keine Zufallsmonster +- ZERSTÖRE Befehl abgeschafft. +- BEKLAUE Befehl abgeschafft. diff --git a/conf/ex/terrains.json b/conf/ex/terrains.json new file mode 100644 index 000000000..eb1019fe1 --- /dev/null +++ b/conf/ex/terrains.json @@ -0,0 +1,286 @@ +{ + "terrains": { + "ocean": { + "size": 10, + "flags": [ "swim", "sea", "sail", "fly" ] + }, + "plain": { + "size": 2000, + "herbs": [ "h0", "h1", "h2", "h3", "h4", "h5" ], + "seed": 3, + "road": 50, + "flags": [ "forest", "cavalry", "land", "walk", "sail", "fly" ], + "production": { + "iron": { + "chance": 0.1, + "base": "5d8", + "div": "2d20+10", + "level": "2d4-1" + }, + "stone": { + "chance": 0.15, + "base": "5d8", + "div": "2d30+20", + "level": "1d4" + }, + "laen": { + "chance": 0.01, + "base": "1d4", + "div": "2d20+50", + "level": "1d4" + } + } + }, + "swamp": { + "size": 200, + "herbs": [ "h6", "h7", "h8" ], + "seed": 2, + "road": 75, + "flags": [ "land", "walk", "sail", "fly" ], + "production": { + "iron": { + "chance": 0.02, + "base": "5d8", + "div": "2d20+10", + "level": "2d4-1" + }, + "stone": { + "chance": 0.02, + "base": "5d8", + "div": "2d30+20", + "level": "1d4" + }, + "laen": { + "chance": 0.02, + "base": "1d4", + "div": "2d20+50", + "level": "1d4" + } + } + }, + "desert": { + "size": 50, + "herbs": [ "h9", "h10", "h11" ], + "seed": 2, + "road": 100, + "flags": [ "land", "walk", "sail", "fly", "cavalry" ], + "production": { + "iron": { + "chance": 0.15, + "base": "5d8", + "div": "2d20+10", + "level": "2d4-1" + }, + "stone": { + "chance": 0.25, + "base": "5d8", + "div": "2d30+20", + "level": "1d4" + }, + "laen": { + "chance": 0.025, + "base": "1d4", + "div": "2d20+50", + "level": "1d4" + } + } + }, + "highland": { + "size": 800, + "herbs": [ "h12", "h13", "h14" ], + "seed": 2, + "road": 100, + "flags": [ "land", "walk", "sail", "fly", "cavalry" ], + "production": { + "iron": { + "chance": 0.15, + "base": "5d8", + "div": "2d20+10", + "level": "2d4-1" + }, + "stone": { + "chance": 0.25, + "base": "5d8", + "div": "2d30+20", + "level": "1d4" + }, + "laen": { + "chance": 0.025, + "base": "1d4", + "div": "2d20+50", + "level": "1d4" + } + } + }, + "mountain": { + "size": 100, + "herbs": [ "h15", "h16", "h17" ], + "seed": 2, + "road": 250, + "flags": [ "land", "walk", "sail", "fly" ], + "production": { + "iron": { + "chance": 1.0, + "base": "50", + "div": "50", + "level": "1" + }, + "stone": { + "chance": 1.0, + "base": "100", + "div": "100", + "level": "1" + }, + "laen": { + "chance": 0.05, + "base": "4", + "div": "100", + "level": "1" + } + } + }, + "glacier": { + "size": 10, + "herbs": [ "h18", "h19", "h20" ], + "seed": 2, + "road": 250, + "flags": [ "arctic", "land", "walk", "sail", "fly" ], + "production": { + "iron": { + "chance": 1.0, + "base": "3", + "div": "50", + "level": "1" + }, + "stone": { + "chance": 1.0, + "base": "2", + "div": "100", + "level": "1" + }, + "laen": { + "chance": 0.05, + "base": "4", + "div": "100", + "level": "1" + } + } + }, + "iceberg": { + "size": 10, + "herbs": [ "h18", "h19", "h20" ], + "flags": [ "arctic", "land", "walk", "sail", "fly" ], + "production": { + "iron": { + "chance": 0.9, + "base": "3", + "div": "50", + "level": "1" + }, + "stone": { + "chance": 0.9, + "base": "2", + "div": "100", + "level": "1" + } + } + }, + "iceberg_sleep": { + "size": 10, + "herbs": [ "h18", "h19", "h20" ], + "flags": [ "arctic", "land", "walk", "sail", "fly" ], + "production": { + "iron": { + "chance": 0.9, + "base": "3", + "div": "50", + "level": "1" + }, + "stone": { + "chance": 0.9, + "base": "2", + "div": "100", + "level": "1" + }, + "laen": { + "chance": 0.05, + "base": "4", + "div": "100", + "level": "1" + } + } + }, + "firewall": { + "flags": [ "forbidden" ] + }, + "fog": { + "flags": [ "walk", "fly" ] + }, + "thickfog": { + "flags": [ "forbidden" ] + }, + "volcano": { + "size": 50, + "road": 250, + "seed": 1, + "flags": [ "land", "walk", "sail", "fly" ], + "production": { + "iron": { + "chance": 0.5, + "level": "1", + "base": "50", + "div": "50" + }, + "stone": { + "chance": 0.5, + "level": "1", + "base": "100", + "div": "100" + }, + "laen": { + "chance": 0.075, + "level": "1", + "base": "4", + "div": "100" + } + } + }, + "activevolcano": { + "size": 50, + "road": 250, + "flags": [ "land", "walk", "sail", "fly" ], + "production": { + "iron": { + "chance": 0.5, + "level": "1", + "base": "50", + "div": "50" + }, + "stone": { + "chance": 0.5, + "level": "1", + "base": "100", + "div": "100" + }, + "laen": { + "chance": 0.075, + "level": "1", + "base": "4", + "div": "100" + } + } + }, + "hell": { + "flags": [ "walk" ] + }, + "hall1": { + "flags": [ "land", "walk", "sail" ] + }, + "corridor1": { + "flags": [ "land", "walk", "sail" ] + }, + "wall1": { + "flags": [ "forbidden", "land" ] + } + } +} diff --git a/res/e3a/spells.xml b/res/e3a/spells.xml index d72386803..64172390f 100644 --- a/res/e3a/spells.xml +++ b/res/e3a/spells.xml @@ -595,7 +595,7 @@ <spell name="shockwave" rank="5" variable="true" combat="2"> <resource name="aura" amount="1" cost="level"/> </spell> - <spell name="eternal_walls" rank="5" parameters="b" ship="true" variable="true"> + <spell name="eternal_walls" rank="5" parameters="b" ship="true"> <resource name="aura" amount="50" cost="fixed"/> <resource name="permaura" amount="1" cost="fixed"/> </spell> diff --git a/res/eressea/spells.xml b/res/eressea/spells.xml index a56b147ef..0570fa80d 100644 --- a/res/eressea/spells.xml +++ b/res/eressea/spells.xml @@ -214,7 +214,7 @@ <spell name="view_reality" rank="5"> <resource name="aura" amount="40" cost="fixed"/> </spell> - <spell name="astral_disruption" rank="4" variable="true"> + <spell name="astral_disruption" rank="4"> <resource name="aura" amount="140" cost="fixed"/> </spell> <spell name="seduction" rank="5" parameters="u" los="true"> @@ -422,7 +422,7 @@ <resource name="aura" amount="1" cost="fixed"/> <resource name="permaura" amount="1" cost="fixed"/> </spell> - <spell name="eternal_walls" rank="5" parameters="b" ship="true" variable="true"> + <spell name="eternal_walls" rank="5" parameters="b" ship="true"> <resource name="aura" amount="50" cost="fixed"/> <resource name="permaura" amount="1" cost="fixed"/> </spell> diff --git a/res/translations/messages.de.po b/res/translations/messages.de.po index 2cd71ecbc..f4a70e083 100644 --- a/res/translations/messages.de.po +++ b/res/translations/messages.de.po @@ -1161,7 +1161,7 @@ msgid "spyreport_faction" msgstr "\"$unit($target) gehört der Partei $faction($faction) an.\"" msgid "ship_drift" -msgstr "\"Die $ship($ship) treibt nach $direction($dir).\"" +msgstr "\"Die $ship($ship) hat zu wenig Segler und treibt nach $direction($dir).\"" msgid "error_max_magicians" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Es kann maximal $int($amount) Magier pro Partei geben.\"" diff --git a/res/translations/messages.en.po b/res/translations/messages.en.po index 1ab2b1608..0e1d32a51 100644 --- a/res/translations/messages.en.po +++ b/res/translations/messages.en.po @@ -1161,7 +1161,7 @@ msgid "spyreport_faction" msgstr "\"$unit($target) belongs to $faction($faction).\"" msgid "ship_drift" -msgstr "\"The ship $ship($ship) drifts to the $direction($dir).\"" +msgstr "\"The ship $ship($ship) needs more sailors and drifts to the $direction($dir).\"" msgid "error_max_magicians" msgstr "\"$unit($unit) in $region($region): '$order($command)' - There may not be more than $int($amount) magicians in your faction.\"" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 76764f0bd..f14303e6c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -92,7 +92,6 @@ set (PARSER_SRC set (ERESSEA_SRC vortex.c - academy.c alchemy.c automate.c battle.c @@ -168,6 +167,7 @@ set(SERVER_SRC bindings.c console.c helpers.c + signals.c main.c ) @@ -179,8 +179,10 @@ set (SERVER_SRC ${SERVER_SRC} ) endif(CURSES_FOUND) -find_program(IWYU_PATH NAMES include-what-you-use iwyu) -if(NOT IWYU_PATH) +#find_program(IWYU_PATH NAMES include-what-you-use iwyu) +if(IWYU_PATH) + # set(C_INCLUDE_WHAT_YOU_USE "${IWYU_PATH} -Xiwyu --no_fwd_decls") +else(IWYU_PATH) message(STATUS "Could not find the program include-what-you-use") endif() @@ -212,7 +214,6 @@ target_link_libraries(eressea ) set(TESTS_SRC - academy.test.c alchemy.test.c automate.test.c battle.test.c diff --git a/src/academy.c b/src/academy.c deleted file mode 100644 index 630552b10..000000000 --- a/src/academy.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "platform.h" -#include "kernel/config.h" -#include <kernel/unit.h> -#include <kernel/building.h> -#include <kernel/item.h> -#include <kernel/pool.h> - -#include "academy.h" -#include "study.h" - -void academy_teaching_bonus(struct unit *u, skill_t sk, int students) { - if (students > 0 && sk != NOSKILL) { - /* actually students * EXPERIENCEDAYS / MAX_STUDENTS */ - learn_skill(u, sk, students); - } -} - -bool academy_can_teach(unit *teacher, unit *scholar, skill_t sk) { - const struct building_type *btype = bt_find("academy"); - return (active_building(scholar, btype)); -} - diff --git a/src/academy.h b/src/academy.h deleted file mode 100644 index 3c496d8fa..000000000 --- a/src/academy.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef H_ACADEMY -#define H_ACADEMY - -#include <skill.h> - -#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 *scholar, skill_t sk); -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/academy.test.c b/src/academy.test.c deleted file mode 100644 index 6f26cee93..000000000 --- a/src/academy.test.c +++ /dev/null @@ -1,52 +0,0 @@ -#include <platform.h> - -#include "academy.h" -#include "skill.h" - -#include <kernel/config.h> -#include <kernel/building.h> -#include <kernel/faction.h> -#include <kernel/unit.h> -#include <kernel/item.h> -#include <kernel/region.h> - -#include <CuTest.h> -#include "tests.h" - -static void test_academy(CuTest * tc) -{ - faction *f; - unit *u, *u2; - region *r; - building *b; - const item_type *it_silver; - - test_setup(); - config_set_int("skills.cost.alchemy", 100); - r = test_create_region(0, 0, NULL); - f = test_create_faction(NULL); - u = test_create_unit(f, r); - b = test_create_building(r, test_create_buildingtype("academy")); - u2 = test_create_unit(f, r); - it_silver = test_create_silver(); - - CuAssert(tc, "teacher must be in academy", !academy_can_teach(u, u2, SK_CROSSBOW)); - u_set_building(u, b); - CuAssert(tc, "student must be in academy", !academy_can_teach(u, u2, SK_CROSSBOW)); - u_set_building(u2, b); - CuAssert(tc, "student must have 50 silver", !academy_can_teach(u, u2, SK_CROSSBOW)); - i_change(&u2->items, it_silver, 50); - CuAssert(tc, "building must be maintained", !academy_can_teach(u, u2, SK_CROSSBOW)); - b->flags |= BLD_MAINTAINED; - CuAssert(tc, "building must have capacity", !academy_can_teach(u, u2, SK_CROSSBOW)); - b->size = 2; - CuAssertTrue(tc, academy_can_teach(u, u2, SK_CROSSBOW)); - test_teardown(); -} - -CuSuite *get_academy_suite(void) -{ - CuSuite *suite = CuSuiteNew(); - SUITE_ADD_TEST(suite, test_academy); - return suite; -} diff --git a/src/alchemy.c b/src/alchemy.c index bc66cb931..bd4220bdb 100644 --- a/src/alchemy.c +++ b/src/alchemy.c @@ -73,7 +73,6 @@ void herbsearch(unit * u, int max_take) int herbsfound; const item_type *whichherb; int effsk = effskill(u, SK_HERBALISM, NULL); - int herbs = rherbs(r); if (effsk == 0) { cmistake(u, u->thisorder, 59, MSG_PRODUCE); @@ -91,13 +90,10 @@ void herbsearch(unit * u, int max_take) return; } - if (max_take < herbs) { - herbs = max_take; - } herbsfound = ntimespprob(effsk * u->number, (double)rherbs(r) / 100.0F, -0.01F); - if (herbsfound > herbs) herbsfound = herbs; + if (herbsfound > max_take) herbsfound = max_take; rsetherbs(r, rherbs(r) - herbsfound); if (herbsfound) { diff --git a/src/automate.c b/src/automate.c index ecb130fff..24657f893 100644 --- a/src/automate.c +++ b/src/automate.c @@ -6,6 +6,8 @@ #include "kernel/order.h" #include "kernel/region.h" #include "kernel/unit.h" +#include "kernel/pool.h" +#include "kernel/item.h" #include "util/keyword.h" #include "util/log.h" @@ -210,7 +212,12 @@ void do_autostudy(region *r) autostudy_run(scholars, nscholars); for (i = 0; i != nscholars; ++i) { int days = STUDYDAYS * scholars[i].learn; - learn_skill(scholars[i].u, skill, days); + int money = learn_skill(scholars[i].u, skill, days, 0); + if (money > 0) { + use_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT, money); + ADDMSG(&u->faction->msgs, msg_message("studycost", + "unit region cost skill", u, u->region, money, skill)); + } } } } diff --git a/src/battle.c b/src/battle.c index 7e3373019..2d45c6df8 100644 --- a/src/battle.c +++ b/src/battle.c @@ -899,7 +899,7 @@ void drain_exp(struct unit *u, int n) } } if (sk != NOSKILL) { - reduce_skill_days(u, sk, n); + change_skill_days(u, sk, -n); } } diff --git a/src/bindings.c b/src/bindings.c index 6d2481a29..453c29d08 100755 --- a/src/bindings.c +++ b/src/bindings.c @@ -310,18 +310,6 @@ static int tolua_create_curse(lua_State * L) return 1; } -static int tolua_learn_skill(lua_State * L) -{ - unit *u = (unit *)tolua_tousertype(L, 1, 0); - const char *skname = tolua_tostring(L, 2, 0); - int days = (int)tolua_tonumber(L, 3, 0); - skill_t sk = findskill(skname); - if (sk != NOSKILL) { - learn_skill(u, sk, days); - } - return 0; -} - static int tolua_update_scores(lua_State * L) { UNUSED_ARG(L); @@ -973,7 +961,6 @@ int tolua_bindings_open(lua_State * L, const dictionary *inifile) tolua_function(L, TOLUA_CAST "remove_empty_units", tolua_remove_empty_units); tolua_function(L, TOLUA_CAST "update_scores", tolua_update_scores); tolua_function(L, TOLUA_CAST "update_owners", tolua_update_owners); - tolua_function(L, TOLUA_CAST "learn_skill", tolua_learn_skill); tolua_function(L, TOLUA_CAST "create_curse", tolua_create_curse); tolua_function(L, TOLUA_CAST "translate", &tolua_translate); tolua_function(L, TOLUA_CAST "spells", tolua_get_spells); diff --git a/src/creport.c b/src/creport.c index 4fb9a50b2..551ee7fa9 100644 --- a/src/creport.c +++ b/src/creport.c @@ -1363,9 +1363,11 @@ static void cr_output_region(FILE * F, report_context * ctx, region * r) fprintf(F, "%d;Rekruten\n", rpeasants(r) / RECRUITFRACTION); } if (max_production(r)) { - int p_wage = wage(r, NULL, NULL, turn + 1); + /* Im CR steht der Bauernlohn, der bei Trauer nur 10 ist */ + bool mourn = is_mourning(r, turn); + int p_wage = peasant_wage(r, mourn); fprintf(F, "%d;Lohn\n", p_wage); - if (is_mourning(r, turn + 1)) { + if (mourn) { fputs("1;mourning\n", F); } } diff --git a/src/economy.c b/src/economy.c index d7a1b9373..fd87ee20c 100644 --- a/src/economy.c +++ b/src/economy.c @@ -1993,7 +1993,8 @@ expandwork(region * r, econ_request * work_begin, econ_request * work_end, int m /* n: verbleibende Einnahmen */ /* fishes: maximale Arbeiter */ int jobs = maxwork; - int p_wage = wage(r, NULL, NULL, turn); + bool mourn = is_mourning(r, turn); + int p_wage = peasant_wage(r, mourn); int money = rmoney(r); if (total > 0 && !rule_autowork()) { econ_request *o; @@ -2017,7 +2018,7 @@ expandwork(region * r, econ_request * work_begin, econ_request * work_end, int m assert(workers >= 0); - u->n = workers * wage(u->region, u->faction, u_race(u), turn); + u->n = workers * wage(u->region, u_race(u)); jobs -= workers; assert(jobs >= 0); @@ -2061,7 +2062,7 @@ static int work_cmd(unit * u, order * ord, econ_request ** io_req) } return 0; } - w = wage(r, u->faction, u_race(u), turn); + w = wage(r, u_race(u)); add_request(req++, ECON_WORK, u, ord, w * u->number); *io_req = req; return u->number; diff --git a/src/items/xerewards.c b/src/items/xerewards.c index e0ce80802..25b1acc0b 100644 --- a/src/items/xerewards.c +++ b/src/items/xerewards.c @@ -37,7 +37,7 @@ struct order *ord) skill *sv = u->skills; while (sv != u->skills + u->skill_size) { /* only one person learns for 3 weeks */ - learn_skill(u, (skill_t)sv->id, STUDYDAYS * 3); + change_skill_days(u, (skill_t)sv->id, STUDYDAYS * 3); ++sv; } } diff --git a/src/items/xerewards.test.c b/src/items/xerewards.test.c index d135b27fb..c8bda3f04 100644 --- a/src/items/xerewards.test.c +++ b/src/items/xerewards.test.c @@ -52,17 +52,17 @@ static void test_skillpotion(CuTest *tc) { itype = test_create_itemtype("skillpotion"); change_resource(u, itype->rtype, 2); - learn_skill(u, SK_ENTERTAINMENT, STUDYDAYS * u->number); + change_skill_days(u, SK_ENTERTAINMENT, STUDYDAYS * u->number); pSkill = unit_skill(u, SK_ENTERTAINMENT); sk_set(pSkill, 5); initialWeeks_Entertainment = pSkill->weeks = 4; - learn_skill(u, SK_STAMINA, STUDYDAYS * u->number); + change_skill_days(u, SK_STAMINA, STUDYDAYS * u->number); pSkill = unit_skill(u, SK_STAMINA); sk_set(pSkill, 5); initialWeeks_Stamina = pSkill->weeks = 4; - learn_skill(u, SK_MAGIC, STUDYDAYS * u->number); + change_skill_days(u, SK_MAGIC, STUDYDAYS * u->number); pSkill = unit_skill(u, SK_MAGIC); sk_set(pSkill, 5); initialWeeks_Magic = pSkill->weeks = 4; diff --git a/src/kernel/building.c b/src/kernel/building.c index a2283c1cc..f93f28948 100644 --- a/src/kernel/building.c +++ b/src/kernel/building.c @@ -495,20 +495,22 @@ extern struct attrib_type at_icastle; /** returns the building's build stage (NOT size in people). * only makes sense for castles or similar buildings with multiple * stages */ -int buildingeffsize(const building * b, int img) +int buildingeffsize(const building * b, bool imaginary) { const struct building_type *btype = NULL; if (b == NULL) return 0; - btype = b->type; - if (img) { + if (imaginary) { const attrib *a = a_find(b->attribs, &at_icastle); if (a) { btype = (const struct building_type *)a->data.v; } } + else { + btype = b->type; + } return bt_effsize(btype, b, b->size); } @@ -518,7 +520,7 @@ int bt_effsize(const building_type * btype, const building * b, int bsize) bsize = adjust_size(b, bsize); } - if (btype->stages) { + if (btype && btype->stages) { int n = 0; const building_stage *stage = btype->stages; do { @@ -728,7 +730,7 @@ static const int wagetable[7][3] = { }; static int -default_wage(const region * r, const faction * f, const race * rc, int in_turn) +default_wage(const region * r, const race * rc) { building *b = largestbuilding(r, cmp_wage, false); int esize = 0; @@ -739,25 +741,23 @@ default_wage(const region * r, const faction * f, const race * rc, int in_turn) esize = buildingeffsize(b, false); } - if (f != NULL) { + if (rc != NULL) { + static const struct race *rc_orc, *rc_snotling; + static int rc_cache; int index = 0; - if (rc == get_race(RC_ORC) || rc == get_race(RC_SNOTLING)) { + if (rc_changed(&rc_cache)) { + rc_orc = get_race(RC_ORC); + rc_snotling = get_race(RC_SNOTLING); + } + if (rc == rc_orc || rc == rc_snotling) { index = 1; } wage = wagetable[esize][index]; } else { - if (is_mourning(r, in_turn)) { - wage = 10; - } - else if (fval(r->terrain, SEA_REGION)) { - wage = 11; - } - else { - wage = wagetable[esize][2]; - } - if (r->attribs && rule_blessed_harvest() == HARVEST_WORK) { - /* E1 rules */ + wage = wagetable[esize][2]; + if (rule_blessed_harvest() & HARVEST_WORK) { + /* Ge�ndert in E3 */ wage += harvest_effect(r); } } @@ -766,7 +766,7 @@ default_wage(const region * r, const faction * f, const race * rc, int in_turn) attrib *a; curse *c; variant vm; - + /* Godcurse: Income -10 */ vm = frac_make(wage, 1); @@ -786,31 +786,37 @@ default_wage(const region * r, const faction * f, const race * rc, int in_turn) } static int -minimum_wage(const region * r, const faction * f, const race * rc, int in_turn) +minimum_wage(const region * r, const race * rc) { - if (f && rc) { + if (rc) { return rc->maintenance; } - return default_wage(r, f, rc, in_turn); + return default_wage(r, rc); } /** * Gibt Arbeitslohn fuer entsprechende Rasse zurueck, oder fuer - * die Bauern wenn f == NULL. */ -int wage(const region * r, const faction * f, const race * rc, int in_turn) + * die Bauern wenn rc == NULL. */ +int wage(const region * r, const race * rc) { static int config; static int rule_wage; if (config_changed(&config)) { rule_wage = config_get_int("rules.wage.function", 1); } - if (rule_wage==0) { + if (rule_wage == 0) { return 0; } - if (rule_wage==1) { - return default_wage(r, f, rc, in_turn); + + if (rule_wage == 1) { + return default_wage(r, rc); } - return minimum_wage(r, f, rc, in_turn); + return minimum_wage(r, rc); +} + +int peasant_wage(const struct region *r, bool mourn) +{ + return mourn ? 10 : wage(r, NULL); } int cmp_wage(const struct building *b, const building * a) diff --git a/src/kernel/building.h b/src/kernel/building.h index 74128a036..dfe1b544a 100644 --- a/src/kernel/building.h +++ b/src/kernel/building.h @@ -116,8 +116,8 @@ extern "C" { int id, int size, struct order *ord); bool building_finished(const struct building *b); - int wage(const struct region *r, const struct faction *f, - const struct race *rc, int in_turn); + int wage(const struct region *r, const struct race *rc); + int peasant_wage(const struct region *r, bool mourn); typedef int(*cmp_building_cb) (const struct building * b, const struct building * a); @@ -130,7 +130,7 @@ extern "C" { int building_taxes(const building *b); /* old functions, still in build.c: */ - int buildingeffsize(const building * b, int imaginary); + int buildingeffsize(const building * b, bool imaginary); void bhash(struct building *b); void bunhash(struct building *b); int buildingcapacity(const struct building *b); diff --git a/src/kernel/building.test.c b/src/kernel/building.test.c index daa75b496..91806ba3d 100644 --- a/src/kernel/building.test.c +++ b/src/kernel/building.test.c @@ -434,6 +434,80 @@ static void test_cmp_castle_size(CuTest *tc) { test_teardown(); } +static void test_wage(CuTest *tc) { + region *r; + building *b; + building_type *btype; + struct building_stage *stage; + race *rc_orc, *rc_elf; + test_setup(); + rc_orc = test_create_race("orc"); + rc_elf = test_create_race("elf"); + rc_elf->maintenance = 13; + btype = test_create_buildingtype("castle"); + stage = btype->stages; + stage->construction->maxsize = 2; /* site */ + stage = stage->next = calloc(1, sizeof(struct building_stage)); + stage->construction = calloc(1, sizeof(struct construction)); + stage->construction->maxsize = 8; /* tradepost */ + stage = stage->next = calloc(1, sizeof(struct building_stage)); + stage->construction = calloc(1, sizeof(struct construction)); + stage->construction->maxsize = 40; /* fortification */ + stage = stage->next = calloc(1, sizeof(struct building_stage)); + stage->construction = calloc(1, sizeof(struct construction)); + stage->construction->maxsize = 200; /* fortification */ + r = test_create_plain(0, 0); + CuAssertIntEquals(tc, 10, wage(r, rc_elf)); + CuAssertIntEquals(tc, 10, wage(r, rc_orc)); + CuAssertIntEquals(tc, 11, peasant_wage(r, false)); + CuAssertIntEquals(tc, 10, peasant_wage(r, true)); + + b = test_create_building(r, btype); + b->size = 1; + CuAssertIntEquals(tc, 0, buildingeffsize(b, false)); + CuAssertIntEquals(tc, 10, wage(r, rc_elf)); + CuAssertIntEquals(tc, 10, wage(r, rc_orc)); + CuAssertIntEquals(tc, 11, peasant_wage(r, false)); + CuAssertIntEquals(tc, 10, peasant_wage(r, true)); + b->size = 2; + CuAssertIntEquals(tc, 1, buildingeffsize(b, false)); + b->size = 9; + CuAssertIntEquals(tc, 1, buildingeffsize(b, false)); + CuAssertIntEquals(tc, 10, wage(r, rc_elf)); + CuAssertIntEquals(tc, 10, wage(r, rc_orc)); + CuAssertIntEquals(tc, 11, peasant_wage(r, false)); + CuAssertIntEquals(tc, 10, peasant_wage(r, true)); + b->size = 10; + CuAssertIntEquals(tc, 2, buildingeffsize(b, false)); + b->size = 49; + CuAssertIntEquals(tc, 2, buildingeffsize(b, false)); + CuAssertIntEquals(tc, 11, wage(r, rc_elf)); + CuAssertIntEquals(tc, 11, wage(r, rc_orc)); + CuAssertIntEquals(tc, 12, peasant_wage(r, false)); + CuAssertIntEquals(tc, 10, peasant_wage(r, true)); + b->size = 50; + CuAssertIntEquals(tc, 3, buildingeffsize(b, false)); + b->size = 249; + CuAssertIntEquals(tc, 3, buildingeffsize(b, false)); + CuAssertIntEquals(tc, 12, wage(r, rc_elf)); + CuAssertIntEquals(tc, 11, wage(r, rc_orc)); + CuAssertIntEquals(tc, 13, peasant_wage(r, false)); + CuAssertIntEquals(tc, 10, peasant_wage(r, true)); + b->size = 250; + CuAssertIntEquals(tc, 4, buildingeffsize(b, false)); + CuAssertIntEquals(tc, 13, wage(r, rc_elf)); + CuAssertIntEquals(tc, 12, wage(r, rc_orc)); + CuAssertIntEquals(tc, 14, peasant_wage(r, false)); + CuAssertIntEquals(tc, 10, peasant_wage(r, true)); + config_set_int("rules.wage.function", 1); + CuAssertIntEquals(tc, 13, wage(r, rc_elf)); + config_set_int("rules.wage.function", 0); + CuAssertIntEquals(tc, 0, wage(r, rc_elf)); + config_set_int("rules.wage.function", 2); + CuAssertIntEquals(tc, rc_elf->maintenance, wage(r, rc_elf)); + test_teardown(); +} + static void test_cmp_wage(CuTest *tc) { region *r; building *b1, *b2; @@ -619,6 +693,7 @@ CuSuite *get_building_suite(void) SUITE_ADD_TEST(suite, test_cmp_castle_size); SUITE_ADD_TEST(suite, test_cmp_taxes); SUITE_ADD_TEST(suite, test_cmp_wage); + SUITE_ADD_TEST(suite, test_wage); SUITE_ADD_TEST(suite, test_cmp_current_owner); SUITE_ADD_TEST(suite, test_register_building); SUITE_ADD_TEST(suite, test_btype_defaults); diff --git a/src/kernel/config.h b/src/kernel/config.h index 9b10f1520..4ee59406b 100644 --- a/src/kernel/config.h +++ b/src/kernel/config.h @@ -42,7 +42,7 @@ extern "C" { bool rule_stealth_anon(void); /* units can anonymize their faction, TARNE PARTEI [NICHT] */ int rule_alliance_limit(void); int rule_faction_limit(void); -#define HARVEST_WORK 0x00 +#define HARVEST_WORK 0x02 #define HARVEST_TAXES 0x01 int rule_blessed_harvest(void); #define GIVE_SELF 1 diff --git a/src/kernel/region.c b/src/kernel/region.c index 6f700ae03..bd5b66287 100644 --- a/src/kernel/region.c +++ b/src/kernel/region.c @@ -602,7 +602,7 @@ int rpeasants(const region * r) return value; } -void rsetpeasants(region * r, int value) +int rsetpeasants(region * r, int value) { assert(r->land || value==0); assert(value >= 0); @@ -612,7 +612,9 @@ void rsetpeasants(region * r, int value) value = USHRT_MAX; } r->land->peasants = (unsigned short)value; + return r->land->peasants; } + return 0; } int rmoney(const region * r) @@ -746,17 +748,16 @@ int rsettrees(const region * r, int ageclass, int value) { if (!r->land) { assert(value == 0); + return 0; + } + assert(value >= 0); + if (value < MAXTREES) { + r->land->trees[ageclass] = value; } else { - assert(value >= 0); - if (value <= MAXTREES) { - return r->land->trees[ageclass] = value; - } - else { - r->land->trees[ageclass] = MAXTREES; - } + r->land->trees[ageclass] = MAXTREES; } - return 0; + return r->land->trees[ageclass]; } region *region_create(int uid) @@ -1095,11 +1096,10 @@ void init_region(region *r) if (!fval(r, RF_CHAOTIC)) { int peasants; + int p_wage = 1 + peasant_wage(r, false) + rng_int() % 5; peasants = (region_maxworkers(r) * (20 + dice(6, 10))) / 100; if (peasants < 100) peasants = 100; - rsetpeasants(r, peasants); - rsetmoney(r, rpeasants(r) * ((wage(r, NULL, NULL, - INT_MAX) + 1) + rng_int() % 5)); + rsetmoney(r, rsetpeasants(r, peasants) * p_wage); } } diff --git a/src/kernel/region.h b/src/kernel/region.h index 89b240f1b..9a7563712 100644 --- a/src/kernel/region.h +++ b/src/kernel/region.h @@ -176,7 +176,7 @@ extern "C" { int rsettrees(const struct region *r, int ageclass, int value); int rpeasants(const struct region *r); - void rsetpeasants(struct region *r, int value); + int rsetpeasants(struct region *r, int value); int rmoney(const struct region *r); void rsetmoney(struct region *r, int value); int rhorses(const struct region *r); diff --git a/src/kernel/resources.c b/src/kernel/resources.c index 17cec5ea2..6c93a0a13 100644 --- a/src/kernel/resources.c +++ b/src/kernel/resources.c @@ -118,22 +118,34 @@ static void terraform_default(struct rawmaterial *res, const region * r) } static int visible_default(const rawmaterial * res, int skilllevel) -/* resources are visible, if skill equals minimum skill to mine them +/* resources are visible if skill equals minimum skill to mine them * plus current level of difficulty */ { const struct item_type *itype = res->rtype->itype; + int level = res->level + itype->construction->minskill - 1; if (res->level <= 1 - && res->level + itype->construction->minskill <= skilllevel + 1) { + && level <= skilllevel) { assert(res->amount > 0); return res->amount; } - else if (res->level + itype->construction->minskill <= skilllevel + 2) { + else if (level < skilllevel) { assert(res->amount > 0); return res->amount; } return -1; } +static int visible_half_skill(const rawmaterial * res, int skilllevel) +/* resources are visible if skill equals half as much as normal */ +{ + const struct item_type *itype = res->rtype->itype; + int level = res->level + itype->construction->minskill - 1; + if (2 * skilllevel >= level) { + return res->amount; + } + return -1; +} + static void use_default(rawmaterial * res, const region * r, int amount) { assert(res->amount > 0 && amount >= 0 && amount <= res->amount); @@ -171,13 +183,19 @@ struct rawmaterial_type *rmt_get(const struct resource_type *rtype) struct rawmaterial_type *rmt_create(struct resource_type *rtype) { if (!rtype->raw) { + int rule = config_get_int("resource.visibility.rule", 0); rawmaterial_type *rmtype = rtype->raw = malloc(sizeof(rawmaterial_type)); if (!rmtype) abort(); rmtype->rtype = rtype; rmtype->terraform = terraform_default; rmtype->update = NULL; rmtype->use = use_default; - rmtype->visible = visible_default; + if (rule == 0) { + rmtype->visible = visible_default; + } + else { + rmtype->visible = visible_half_skill; + } } return rtype->raw; } diff --git a/src/kernel/version.c b/src/kernel/version.c index 8c694f7f0..0739b11b1 100644 --- a/src/kernel/version.c +++ b/src/kernel/version.c @@ -8,7 +8,7 @@ #ifndef ERESSEA_VERSION /* the version number, if it was not passed to make with -D */ -#define ERESSEA_VERSION "3.25.0" +#define ERESSEA_VERSION "3.26.0" #endif const char *eressea_version(void) { diff --git a/src/laws.c b/src/laws.c index 3d3d174b5..8ee1e9341 100644 --- a/src/laws.c +++ b/src/laws.c @@ -743,7 +743,8 @@ void immigration(void) /* if less than 50 are in the region and there is space and no monster or demon units in the region */ if (repopulate) { int peasants = rpeasants(r); - int income = wage(r, NULL, NULL, turn) - maintenance_cost(NULL) + 1; + bool mourn = is_mourning(r, turn); + int income = peasant_wage(r, mourn) - maintenance_cost(NULL) + 1; if (income >= 0 && r->land && (peasants < repopulate) && region_maxworkers(r) >(peasants + 30) * 2) { int badunit = 0; unit *u; @@ -754,7 +755,7 @@ void immigration(void) } } if (badunit == 0) { - peasants += (int)(rng_double()*income); + peasants += (int)(rng_double() * income); rsetpeasants(r, peasants); } } @@ -834,8 +835,10 @@ void demographics(void) if (r->age > 20) { double mwp = fmax(region_maxworkers(r), 1); + bool mourn = is_mourning(r, turn); + int p_wage = peasant_wage(r, mourn); double prob = - pow(rpeasants(r) / (mwp * wage(r, NULL, NULL, turn) * 0.13), 4.0) + pow(rpeasants(r) / (mwp * p_wage * 0.13), 4.0) * PLAGUE_CHANCE; if (rng_double() < prob) { diff --git a/src/lighthouse.c b/src/lighthouse.c index 7915bdf44..257836d20 100644 --- a/src/lighthouse.c +++ b/src/lighthouse.c @@ -21,7 +21,12 @@ attrib_type at_lighthouse = { bool is_lighthouse(const building_type *btype) { - return is_building_type(btype, "lighthouse"); + static int config; + static const building_type *bt_lighthouse; + if (bt_changed(&config)) { + bt_lighthouse = bt_find("lighthouse"); + } + return btype == bt_lighthouse; } /* update_lighthouse: call this function whenever the size of a lighthouse changes @@ -81,7 +86,7 @@ void remove_lighthouse(const building *lh) { int lighthouse_range(const building * b) { - if (b->size >= 10 && (b->flags & BLD_MAINTAINED)) { + if (b->size >= 10) { return (int)log10(b->size) + 1; } return 0; @@ -112,13 +117,19 @@ bool lighthouse_guarded(const region * r) for (a = a_find(r->attribs, &at_lighthouse); a && a->type == &at_lighthouse; a = a->next) { building *b = (building *)a->data.v; - - assert(is_building_type(b->type, "lighthouse")); - if ((b->flags & BLD_MAINTAINED) && b->size >= 10) { - int maxd = (int)log10(b->size) + 1; - int d = distance(r, b->region); - assert(maxd >= d); - return true; + if (b->flags & BLD_MAINTAINED) { + if (r == b->region) { + return true; + } + else { + int maxd = lighthouse_range(b); + if (maxd > 0) { + int d = distance(r, b->region); + if (maxd >= d) { + return true; + } + } + } } } diff --git a/src/lighthouse.test.c b/src/lighthouse.test.c index 9dc6d2999..8230b97f2 100644 --- a/src/lighthouse.test.c +++ b/src/lighthouse.test.c @@ -30,8 +30,6 @@ static void test_lighthouse_range(CuTest * tc) b->size = 9; CuAssertIntEquals(tc, 0, lighthouse_range(b)); b->size = 10; - CuAssertIntEquals(tc, 0, lighthouse_range(b)); - b->flags |= BLD_MAINTAINED; CuAssertIntEquals(tc, 2, lighthouse_range(b)); u1->building = b; u2->building = b; @@ -39,6 +37,8 @@ static void test_lighthouse_range(CuTest * tc) set_level(u1, SK_PERCEPTION, 3); set_level(u2, SK_PERCEPTION, 3); + CuAssertIntEquals(tc, 0, lighthouse_view_distance(b, u1)); + b->flags |= BLD_MAINTAINED; CuAssertIntEquals(tc, 1, lighthouse_view_distance(b, u1)); set_level(u1, SK_PERCEPTION, 6); CuAssertIntEquals(tc, 1, lighthouse_view_distance(b, u2)); @@ -126,6 +126,7 @@ static void test_lighthouse_guard(CuTest * tc) { CuAssertIntEquals(tc, true, lighthouse_guarded(r3)); CuAssertIntEquals(tc, false, lighthouse_guarded(r4)); b->size = 1; /* size can go down in destroy_cmd */ + update_lighthouse(b); CuAssertIntEquals(tc, false, lighthouse_guarded(r2)); CuAssertIntEquals(tc, false, lighthouse_guarded(r3)); test_teardown(); diff --git a/src/main.c b/src/main.c index 130a2f6f7..9cf76f262 100644 --- a/src/main.c +++ b/src/main.c @@ -17,6 +17,7 @@ #include "gmtool.h" #endif +#include "signals.h" #include "bindings.h" #include <iniparser.h> @@ -247,39 +248,6 @@ static int parse_args(int argc, char **argv) return 0; } -#ifdef HAVE_BACKTRACE -#include <execinfo.h> -#include <signal.h> -static void *btrace[50]; - -static void report_segfault(int signo, siginfo_t * sinf, void *arg) -{ - size_t size; - int fd = fileno(stderr); - - fflush(stdout); - fputs("\n\nProgram received SIGSEGV, backtrace follows.\n", stderr); - size = backtrace(btrace, 50); - backtrace_symbols_fd(btrace, size, fd); - abort(); -} - -static int setup_signal_handler(void) -{ - struct sigaction act; - - act.sa_flags = SA_RESETHAND | SA_SIGINFO; - act.sa_sigaction = report_segfault; - sigfillset(&act.sa_mask); - return sigaction(SIGSEGV, &act, NULL); -} -#else -static int setup_signal_handler(void) -{ - return 0; -} -#endif - void locale_init(void) { setlocale(LC_CTYPE, ""); diff --git a/src/modules/museum.c b/src/modules/museum.c index 8f8350655..cbaf8d3c0 100644 --- a/src/modules/museum.c +++ b/src/modules/museum.c @@ -124,7 +124,7 @@ order * ord) UNUSED_ARG(amount); /* Pruefen ob in Eingangshalle */ - if (u->region->x != 9525 || u->region->y != 9525) { + if (warden == NULL || u->region->x != 9525 || u->region->y != 9525) { cmistake(u, ord, 266, MSG_MAGIC); return 0; } diff --git a/src/monsters.c b/src/monsters.c index bd156238b..148e6f46d 100644 --- a/src/monsters.c +++ b/src/monsters.c @@ -974,7 +974,11 @@ void spawn_undead(void) { region *r; faction *monsters = get_monsters(); + int spawn_chance = config_get_int("monsters.spawn.chance", 100) * 100; + if (spawn_chance <= 0) { + return; + } for (r = regions; r; r = r->next) { int unburied = deathcount(r); @@ -985,7 +989,7 @@ void spawn_undead(void) } if (r->land && unburied > rpeasants(r) / 20 - && rng_int() % 10000 < 100) { + && rng_int() % spawn_chance < 100) { message *msg; unit *u; /* es ist sinnfrei, wenn irgendwo im Wald 3er-Einheiten Untote entstehen. diff --git a/src/move.c b/src/move.c index 38cc0d8bb..e9bbea9a0 100644 --- a/src/move.c +++ b/src/move.c @@ -1612,9 +1612,9 @@ static const region_list *travel_route(unit * u, /* Berichte ueber Durchreiseregionen */ if (mode != TRAVEL_TRANSPORTED) { + arg_regions ar; arg_regions *arp = NULL; if (steps > 1) { - arg_regions ar; arp = &ar; var_create_regions(arp, route_begin, steps - 1); } @@ -2192,8 +2192,8 @@ void move_cmd_ex(unit * u, order * ord, const char *directions) init_order(ord, u->faction->locale); } if (u->ship && u == ship_owner(u->ship)) { - bool drifting = (getkeyword(ord) == K_MOVE); - sail(u, ord, drifting); + keyword_t kwd = getkeyword(ord); + sail(u, ord, (kwd == K_MOVE || kwd == K_ROUTE)); } else { travel(u, ord); diff --git a/src/report.c b/src/report.c index f283b552f..09b9ab82e 100644 --- a/src/report.c +++ b/src/report.c @@ -1182,11 +1182,13 @@ static void report_statistics(struct stream *out, const region * r, const factio if (max_production(r) && (!fval(r->terrain, SEA_REGION) || f->race == get_race(RC_AQUARIAN))) { if (markets_module()) { /* hack */ + bool mourn = is_mourning(r, turn); + int p_wage = peasant_wage(r, mourn); m = - msg_message("nr_stat_salary_new", "max", wage(r, NULL, NULL, turn + 1)); + msg_message("nr_stat_salary_new", "max", p_wage); } else { - m = msg_message("nr_stat_salary", "max", wage(r, f, f->race, turn + 1)); + m = msg_message("nr_stat_salary", "max", wage(r, f->race)); } nr_render(m, f->locale, buf, sizeof(buf), f); paragraph(out, buf, 2, 2, 0); @@ -1337,7 +1339,7 @@ report_template(const char *filename, report_context * ctx, const char *bom) } rps_nowrap(out, buf); newline(out); - sprintf(buf, "; ECheck Lohn %d", wage(r, f, f->race, turn + 1)); + sprintf(buf, "; ECheck Lohn %d", wage(r, f->race)); rps_nowrap(out, buf); newline(out); newline(out); diff --git a/src/signals.c b/src/signals.c new file mode 100644 index 000000000..7cbe680df --- /dev/null +++ b/src/signals.c @@ -0,0 +1,35 @@ +#ifdef HAVE_BACKTRACE +#include <execinfo.h> +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +static void *btrace[50]; + +static void report_segfault(int signo, siginfo_t * sinf, void *arg) +{ + size_t size; + int fd = fileno(stderr); + + fflush(stdout); + fputs("\n\nProgram received SIGSEGV, backtrace follows.\n", stderr); + size = backtrace(btrace, 50); + backtrace_symbols_fd(btrace, size, fd); + abort(); +} + +int setup_signal_handler(void) +{ + struct sigaction act; + + act.sa_flags = SA_RESETHAND | SA_SIGINFO; + act.sa_sigaction = report_segfault; + sigfillset(&act.sa_mask); + return sigaction(SIGSEGV, &act, 0); +} +#else +int setup_signal_handler(void) +{ + return 0; +} +#endif + diff --git a/src/signals.h b/src/signals.h new file mode 100644 index 000000000..640a766f7 --- /dev/null +++ b/src/signals.h @@ -0,0 +1,4 @@ +#pragma once + +int setup_signal_handler(void); + diff --git a/src/study.c b/src/study.c index 74f3d108c..e41d9185d 100644 --- a/src/study.c +++ b/src/study.c @@ -8,7 +8,6 @@ #include "move.h" #include "monsters.h" #include "alchemy.h" -#include "academy.h" #include "kernel/calendar.h" #include <spells/regioncurse.h> @@ -154,37 +153,37 @@ const attrib_type at_learning = { #define EXPERIENCEDAYS 10 -static int study_days(unit * scholar, skill_t sk) +static int study_days(unit * u, skill_t sk) { int speed = STUDYDAYS; - if (u_race(scholar)->study_speed) { - speed += u_race(scholar)->study_speed[sk]; + if (u_race(u)->study_speed) { + speed += u_race(u)->study_speed[sk]; if (speed < STUDYDAYS) { - skill *sv = unit_skill(scholar, sk); + skill *sv = unit_skill(u, sk); if (sv == 0) { speed = STUDYDAYS; } } } - return scholar->number * speed; + return u->number * speed; } static int -teach_unit(unit * teacher, unit * scholar, int nteaching, skill_t sk, +teach_unit(unit * teacher, unit * student, int nteaching, skill_t sk, bool report, int *academy_students) { teaching_info *teach = NULL; attrib *a; int students; - if (magic_lowskill(scholar)) { + if (magic_lowskill(student)) { cmistake(teacher, teacher->thisorder, 292, MSG_EVENT); return 0; } - students = scholar->number; + students = student->number; /* subtract already taught students */ - a = a_find(scholar->attribs, &at_learning); + a = a_find(student->attribs, &at_learning); if (a != NULL) { teach = (teaching_info *)a->data.v; students -= teach->students; @@ -194,18 +193,16 @@ teach_unit(unit * teacher, unit * scholar, int nteaching, skill_t sk, if (students > 0) { if (teach == NULL) { - a = a_add(&scholar->attribs, a_new(&at_learning)); + a = a_add(&student->attribs, a_new(&at_learning)); teach = (teaching_info *)a->data.v; } selist_push(&teach->teachers, teacher); teach->days += students * STUDYDAYS; teach->students += students; - if (scholar->building) { - /* Solange Akademien groessenbeschraenkt sind, sollte Lehrer und - * Student auch in unterschiedlichen Gebaeuden stehen duerfen */ - /* FIXME comment contradicts implementation */ - if (academy_can_teach(teacher, scholar, sk)) { + if (student->building) { + const struct building_type *btype = bt_find("academy"); + if (active_building(student, btype)) { /* Jeder Schueler zusaetzlich +10 Tage wenn in Uni. */ teach->days += students * EXPERIENCEDAYS; /* learning erhoehen */ /* Lehrer zusaetzlich +1 Tag pro Schueler. */ @@ -258,12 +255,12 @@ int teach_cmd(unit * teacher, struct order *ord) count = 0; +#if TEACH_ALL init_order(ord, NULL); -#if TEACH_ALL if (getparam(teacher->faction->locale) == P_ANY) { skill_t sk; - unit *scholar; + unit *student; skill_t teachskill[MAXSKILLS]; int t = 0; @@ -272,15 +269,15 @@ int teach_cmd(unit * teacher, struct order *ord) teachskill[t] = getskill(teacher->faction->locale); } while (sk != NOSKILL); - for (scholar = r->units; teaching > 0 && scholar; scholar = scholar->next) { - if (LongHunger(scholar)) { + for (student = r->units; teaching > 0 && student; student = student->next) { + if (LongHunger(student)) { continue; } - else if (scholar->faction == teacher->faction) { - if (getkeyword(scholar->thisorder) == K_STUDY) { + else if (student->faction == teacher->faction) { + if (getkeyword(student->thisorder) == K_STUDY) { /* Input ist nun von student->thisorder !! */ - init_order(scholar->thisorder, scholar->faction->locale); - sk = getskill(scholar->faction->locale); + init_order(student->thisorder, student->faction->locale); + sk = getskill(student->faction->locale); if (sk != NOSKILL && teachskill[0] != NOSKILL) { for (t = 0; teachskill[t] != NOSKILL; ++t) { if (sk == teachskill[t]) { @@ -290,20 +287,20 @@ int teach_cmd(unit * teacher, struct order *ord) sk = teachskill[t]; } if (sk != NOSKILL - && effskill_study(teacher, sk) - TEACHDIFFERENCE > effskill_study(scholar, sk)) { - teaching -= teach_unit(teacher, scholar, teaching, sk, true, &academy_students); + && effskill_study(teacher, sk) - TEACHDIFFERENCE > effskill_study(student, sk)) { + teaching -= teach_unit(teacher, student, teaching, sk, true, &academy_students); } } } #ifdef TEACH_FRIENDS - else if (alliedunit(teacher, scholar->faction, HELP_GUARD)) { - if (getkeyword(scholar->thisorder) == K_STUDY) { + else if (alliedunit(teacher, student->faction, HELP_GUARD)) { + if (getkeyword(student->thisorder) == K_STUDY) { /* Input ist nun von student->thisorder !! */ - init_order(scholar->thisorder, scholar->faction->locale); - sk = getskill(scholar->faction->locale); + init_order(student->thisorder, student->faction->locale); + sk = getskill(student->faction->locale); if (sk != NOSKILL - && effskill_study(teacher, sk) - TEACHDIFFERENCE >= effskill(scholar, sk, NULL)) { - teaching -= teach_unit(teacher, scholar, teaching, sk, true, &academy_students); + && effskill_study(teacher, sk) - TEACHDIFFERENCE >= effskill(student, sk, NULL)) { + teaching -= teach_unit(teacher, student, teaching, sk, true, &academy_students); } } } @@ -322,15 +319,15 @@ int teach_cmd(unit * teacher, struct order *ord) while (!parser_end()) { skill_t sk; - unit *scholar; + unit *student; bool feedback; - getunit(r, teacher->faction, &scholar); + getunit(r, teacher->faction, &student); ++count; /* Falls die Unit nicht gefunden wird, Fehler melden */ - if (!scholar) { + if (!student) { char tbuf[20]; const char *uid; const char *token; @@ -362,8 +359,8 @@ int teach_cmd(unit * teacher, struct order *ord) continue; } - feedback = teacher->faction == scholar->faction - || alliedunit(scholar, teacher->faction, HELP_GUARD); + feedback = teacher->faction == student->faction + || alliedunit(student, teacher->faction, HELP_GUARD); /* Neuen Befehl zusammenbauen. TEMP-Einheiten werden automatisch in * ihre neuen Nummern uebersetzt. */ @@ -371,102 +368,63 @@ int teach_cmd(unit * teacher, struct order *ord) strncat(zOrder, " ", sz - 1); --sz; } - sz -= str_strlcpy(zOrder + 4096 - sz, itoa36(scholar->no), sz); + sz -= str_strlcpy(zOrder + 4096 - sz, itoa36(student->no), sz); - if (getkeyword(scholar->thisorder) != K_STUDY) { + if (getkeyword(student->thisorder) != K_STUDY) { ADDMSG(&teacher->faction->msgs, - msg_feedback(teacher, ord, "teach_nolearn", "student", scholar)); + msg_feedback(teacher, ord, "teach_nolearn", "student", student)); continue; } /* Input ist nun von student->thisorder !! */ parser_pushstate(); - init_order(scholar->thisorder, scholar->faction->locale); - sk = getskill(scholar->faction->locale); + init_order(student->thisorder, student->faction->locale); + sk = getskill(student->faction->locale); parser_popstate(); if (sk == NOSKILL) { ADDMSG(&teacher->faction->msgs, - msg_feedback(teacher, ord, "teach_nolearn", "student", scholar)); + msg_feedback(teacher, ord, "teach_nolearn", "student", student)); continue; } - if (effskill_study(scholar, sk) > effskill_study(teacher, sk) + if (effskill_study(student, sk) > effskill_study(teacher, sk) - TEACHDIFFERENCE) { if (feedback) { ADDMSG(&teacher->faction->msgs, msg_feedback(teacher, ord, "teach_asgood", - "student", scholar)); + "student", student)); } continue; } if (sk == SK_MAGIC) { /* ist der Magier schon spezialisiert, so versteht er nur noch * Lehrer seines Gebietes */ - magic_t mage2 = unit_get_magic(scholar); + magic_t mage2 = unit_get_magic(student); if (mage2 != M_GRAY) { magic_t mage1 = unit_get_magic(teacher); if (mage1 != mage2) { if (feedback) { ADDMSG(&teacher->faction->msgs, msg_feedback(teacher, ord, - "error_different_magic", "target", scholar)); + "error_different_magic", "target", student)); } continue; } } } sk_academy = sk; - teaching -= teach_unit(teacher, scholar, teaching, sk, false, &academy_students); + teaching -= teach_unit(teacher, student, teaching, sk, false, &academy_students); } new_order = create_order(K_TEACH, teacher->faction->locale, "%s", zOrder); replace_order(&teacher->orders, ord, new_order); free_order(new_order); /* parse_order & set_order have each increased the refcount */ } - if (academy_students > 0 && sk_academy!=NOSKILL) { - academy_teaching_bonus(teacher, sk_academy, academy_students); + if (academy_students > 0 && sk_academy != NOSKILL) { + change_skill_days(teacher, sk_academy, academy_students); } reset_order(); return 0; } -typedef enum study_rule_t { - STUDY_DEFAULT = 0, - STUDY_FASTER = 1, - STUDY_AUTOTEACH = 2 -} study_rule_t; - -static double study_speedup(unit * u, skill_t s, study_rule_t rule) -{ -#define MINTURN 16 - if (turn > MINTURN) { - if (rule == STUDY_FASTER) { - double learnweeks = 0; - int i; - for (i = 0; i != u->skill_size; ++i) { - skill *sv = u->skills + i; - if (sv->id == s) { - learnweeks = sv->level * (sv->level + 1) / 2.0; - if (learnweeks < turn / 3.0) { - return 2.0; - } - } - } - return 2.0; /* If the skill was not found it is the first study. */ - } - if (rule == STUDY_AUTOTEACH) { - double learnweeks = 0; - int i; - for (i = 0; i != u->skill_size; ++i) { - skill *sv = u->skills + i; - learnweeks += (sv->level * (sv->level + 1) / 2.0); - } - if (learnweeks < turn / 2.0) { - return 2.0; - } - } - } - return 1.0; -} - static bool ExpensiveMigrants(void) { static bool rule; @@ -552,17 +510,12 @@ bool check_student(const struct unit *u, struct order *ord, skill_t sk) { int study_cmd(unit * u, order * ord) { - region *r = u->region; - int p; - int l; int studycost, days; - double multi = 1.0; attrib *a = NULL; teaching_info *teach = NULL; int money = 0; skill_t sk; int maxalchemy = 0; - int speed_rule = (study_rule_t)config_get_int("study.speedup", 0); static const race *rc_snotling; static int rc_cache; @@ -585,7 +538,6 @@ int study_cmd(unit * u, order * ord) } } - p = studycost = study_cost(u, sk); a = a_find(u->attribs, &at_learning); if (a != NULL) { teach = (teaching_info *)a->data.v; @@ -594,16 +546,13 @@ int study_cmd(unit * u, order * ord) /* keine kostenpflichtigen Talente fuer Migranten. Vertraute sind * keine Migranten, wird in is_migrant abgefangen. Vorsicht, * studycost darf hier noch nicht durch Akademie erhoeht sein */ + studycost = study_cost(u, sk); + if (studycost > 0 && !ExpensiveMigrants() && is_migrant(u)) { ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_migrants_nolearn", "")); return -1; } - /* Akademie: */ - if (active_building(u, bt_find("academy"))) { - studycost = studycost * 2; - if (studycost < 50) studycost = 50; - } if (sk == SK_MAGIC) { magic_t mtype; @@ -684,71 +633,31 @@ int study_cmd(unit * u, order * ord) } } } + + days = teach ? teach->days : 0; + days += study_days(u, sk); + if (studycost) { int cost = studycost * u->number; money = get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT, cost); if (money > cost) money = cost; - } - if (money < studycost * u->number) { - studycost = p; /* Ohne Univertreurung */ - if (money > studycost) money = studycost; - if (p > 0 && money < studycost * u->number) { - cmistake(u, ord, 65, MSG_EVENT); - multi = money / (double)(studycost * u->number); + if (money < studycost * u->number) { + int cost = studycost * u->number; + if (money > studycost) money = studycost; + if (money < cost) { + /* we cannot afford everyone, but use as much as we have */ + cmistake(u, ord, 65, MSG_EVENT); + days = days * money / cost; + } } } + money += learn_skill(u, sk, days, studycost); - if (teach == NULL) { - a = a_add(&u->attribs, a_new(&at_learning)); - teach = (teaching_info *)a->data.v; - assert(teach); - teach->teachers = NULL; - } if (money > 0) { use_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT, money); ADDMSG(&u->faction->msgs, msg_message("studycost", "unit region cost skill", u, u->region, money, sk)); } - - if (get_effect(u, oldpotiontype[P_WISE])) { - l = get_effect(u, oldpotiontype[P_WISE]); - if (l > u->number) l = u->number; - teach->days += l * EXPERIENCEDAYS; - change_effect(u, oldpotiontype[P_WISE], -l); - } - if (get_effect(u, oldpotiontype[P_FOOL])) { - l = get_effect(u, oldpotiontype[P_FOOL]); - if (l > u->number) l = u->number; - teach->days -= l * STUDYDAYS; - change_effect(u, oldpotiontype[P_FOOL], -l); - } - - if (p != studycost) { - /* ist_in_gebaeude(r, u, BT_UNIVERSITAET) == 1) { */ - /* p ist Kosten ohne Uni, studycost mit; wenn - * p!=studycost, ist die Einheit zwangsweise - * in einer Uni */ - teach->days += u->number * EXPERIENCEDAYS; - } - - if (is_cursed(r->attribs, &ct_badlearn)) { - teach->days -= u->number * EXPERIENCEDAYS; - } - - multi *= study_speedup(u, sk, speed_rule); - days = study_days(u, sk); - days = (int)((days + teach->days) * multi); - - /* the artacademy currently improves the learning of entertainment - of all units in the region, to be able to make it cumulative with - with an academy */ - - if (sk == SK_ENTERTAINMENT - && buildingtype_exists(r, bt_find("artacademy"), false)) { - days *= 2; - } - - learn_skill(u, sk, days); if (a != NULL) { if (teach->teachers) { msg_teachers(teach->teachers, u, sk); @@ -789,56 +698,133 @@ void produceexp_ex(struct unit *u, skill_t sk, int n, learn_fun learn) } } -void produceexp(struct unit *u, skill_t sk, int n) -{ - produceexp_ex(u, sk, n, learn_skill); -} - static learn_fun inject_learn_fun = 0; void inject_learn(learn_fun fun) { inject_learn_fun = fun; } -/** days should be scaled by u->number; STUDYDAYS * u->number is one week worth of learning */ -void learn_skill(unit *u, skill_t sk, int days) { - int leveldays = STUDYDAYS * u->number; - int weeks = 0; +static void increase_skill_days(unit *u, skill_t sk, int days) { + assert(sk >= 0 && sk < MAXSKILLS && days >= 0); + if (days > 0) { + int leveldays = STUDYDAYS * u->number; + int weeks = 0; + if (inject_learn_fun) { + inject_learn_fun(u, sk, days); + return; + } + while (days >= leveldays) { + ++weeks; + days -= leveldays; + } + if (days > 0 && rng_int() % leveldays < days) { + ++weeks; + } + if (weeks > 0) { + increase_skill(u, sk, weeks); + } + } +} - if (fval(u, UFL_HUNGER)) { - days /= 2; +static void reduce_skill_days(unit *u, skill_t sk, int days) { + if (days > 0) { + skill *sv = unit_skill(u, sk); + if (sv) { + while (days > 0) { + if (days >= STUDYDAYS * u->number) { + reduce_skill(u, sv, 1); + days -= STUDYDAYS; + } + else { + if (chance(days / ((double)STUDYDAYS * u->number))) /* (rng_int() % (30 * u->number) < days)*/ + reduce_skill(u, sv, 1); + days = 0; + } + } + } } - assert(sk >= 0 && sk < MAXSKILLS); - if (inject_learn_fun) { - inject_learn_fun(u, sk, days); - return; +} + +void produceexp(struct unit *u, skill_t sk, int n) +{ + produceexp_ex(u, sk, n, increase_skill_days); +} + +/** + * days should be scaled by u->number; STUDYDAYS * u->number is one week worth of learning + * @return int + * The additional spend, i.e. from an academy. + */ +int learn_skill(unit *u, skill_t sk, int days, int studycost) { + region *r = u->region; + int cost = 0; + + if (r->buildings) { + static const building_type *bt_artacademy; + static const building_type *bt_academy; + static int config; + + if (bt_changed(&config)) { + bt_artacademy = bt_find("artacademy"); + bt_academy = bt_find("academy"); + } + + /* Akademie: */ + if (bt_academy && active_building(u, bt_academy)) { + int avail, n = u->number; + if (studycost < 50) studycost = 50; + cost = studycost * u->number; + avail = get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT, studycost * u->number); + if (avail < cost) { + /* not all students can afford the academy */ + n = n * avail / cost; + cost = n * studycost; + } + days += EXPERIENCEDAYS * n; + } + + /* the artacademy currently improves the learning of entertainment + of all units in the region, to be able to make it cumulative with + with an academy */ + + if (bt_artacademy && sk == SK_ENTERTAINMENT + && buildingtype_exists(r, bt_artacademy, false)) { + days *= 2; + } } - while (days >= leveldays) { - ++weeks; - days -= leveldays; + + if (u->attribs) { + if (get_effect(u, oldpotiontype[P_WISE])) { + int effect = get_effect(u, oldpotiontype[P_WISE]); + if (effect > u->number) effect = u->number; + days += effect * EXPERIENCEDAYS; + change_effect(u, oldpotiontype[P_WISE], -effect); + } + if (get_effect(u, oldpotiontype[P_FOOL])) { + int effect = get_effect(u, oldpotiontype[P_FOOL]); + if (effect > u->number) effect = u->number; + days -= effect * STUDYDAYS; + change_effect(u, oldpotiontype[P_FOOL], -effect); + } } - if (days > 0 && rng_int() % leveldays < days) { - ++weeks; + + if (is_cursed(r->attribs, &ct_badlearn)) { + days -= EXPERIENCEDAYS * u->number; } - if (weeks > 0) { - increase_skill(u, sk, weeks); + + if (fval(u, UFL_HUNGER)) { + days /= 2; } + increase_skill_days(u, sk, days); + return cost; } -void reduce_skill_days(unit *u, skill_t sk, int days) { - skill *sv = unit_skill(u, sk); - if (sv) { - while (days > 0) { - if (days >= STUDYDAYS * u->number) { - reduce_skill(u, sv, 1); - days -= STUDYDAYS; - } - else { - if (chance (days / ((double) STUDYDAYS * u->number))) /* (rng_int() % (30 * u->number) < days)*/ - reduce_skill(u, sv, 1); - days = 0; - } - } +void change_skill_days(unit *u, skill_t sk, int days) { + if (days < 0) { + reduce_skill_days(u, sk, -days); + } + else { + increase_skill_days(u, sk, days); } } @@ -885,7 +871,7 @@ void demon_skillchange(unit *u) } } else { - learn_skill(u, sv->id, STUDYDAYS * u->number * weeks); + change_skill_days(u, sv->id, STUDYDAYS * u->number * weeks); } } ++sv; diff --git a/src/study.h b/src/study.h index 6fed0c50d..dcdc354ca 100644 --- a/src/study.h +++ b/src/study.h @@ -34,8 +34,8 @@ extern "C" { typedef void(*learn_fun)(struct unit *u, skill_t sk, int days); - void learn_skill(struct unit *u, skill_t sk, int days); - void reduce_skill_days(struct unit *u, skill_t sk, int days); + int learn_skill(struct unit *u, skill_t sk, int days, int studycost); + void change_skill_days(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); diff --git a/src/study.test.c b/src/study.test.c index b57c921f4..6b529b252 100644 --- a/src/study.test.c +++ b/src/study.test.c @@ -364,14 +364,14 @@ void test_learn_skill_single(CuTest *tc) { setup_study(); config_set("study.random_progress", "0"); u = test_create_unit(test_create_faction(NULL), test_create_region(0, 0, NULL)); - learn_skill(u, SK_ALCHEMY, STUDYDAYS); + CuAssertIntEquals(tc, 0, learn_skill(u, SK_ALCHEMY, STUDYDAYS, 0)); 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, 0, learn_skill(u, SK_ALCHEMY, STUDYDAYS, 0)); CuAssertIntEquals(tc, 1, sv->weeks); - learn_skill(u, SK_ALCHEMY, STUDYDAYS * 2); + CuAssertIntEquals(tc, 0, learn_skill(u, SK_ALCHEMY, STUDYDAYS * 2, 0)); CuAssertIntEquals(tc, 2, sv->level); CuAssertIntEquals(tc, 2, sv->weeks); test_teardown(); @@ -385,14 +385,14 @@ void test_learn_skill_multi(CuTest *tc) { config_set("study.random_progress", "0"); u = test_create_unit(test_create_faction(NULL), test_create_region(0, 0, NULL)); scale_number(u, 10); - learn_skill(u, SK_ALCHEMY, STUDYDAYS * u->number); + CuAssertIntEquals(tc, 0, learn_skill(u, SK_ALCHEMY, STUDYDAYS * u->number, 0)); 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, 0, learn_skill(u, SK_ALCHEMY, STUDYDAYS * u->number, 0)); CuAssertIntEquals(tc, 1, sv->weeks); - learn_skill(u, SK_ALCHEMY, STUDYDAYS * u->number * 2); + CuAssertIntEquals(tc, 0, learn_skill(u, SK_ALCHEMY, STUDYDAYS * u->number * 2, 0)); CuAssertIntEquals(tc, 2, sv->level); CuAssertIntEquals(tc, 2, sv->weeks); test_teardown(); diff --git a/src/test_eressea.c b/src/test_eressea.c index fc5c49d1c..452b847c5 100644 --- a/src/test_eressea.c +++ b/src/test_eressea.c @@ -93,7 +93,6 @@ int RunAllTests(int argc, char *argv[]) /* items */ ADD_SUITE(xerewards); /* kernel */ - ADD_SUITE(academy); ADD_SUITE(alliance); ADD_SUITE(ally); ADD_SUITE(building); diff --git a/src/util/rand.c b/src/util/rand.c index a14f1def4..6e4e88c89 100644 --- a/src/util/rand.c +++ b/src/util/rand.c @@ -64,7 +64,7 @@ bool chance(double x) { if (x >= 1.0) return true; - return (1-rng_double()) < x; + return rng_double() < x; } typedef struct random_source {