Compare commits

...

22 Commits

Author SHA1 Message Date
Enno Rehling 585969bf98 2706 follow-up
invert the maintenance flag, update lighthouses before writing reports if that hasn't happened before.
2020-09-27 15:51:32 +02:00
Enno Rehling 220045f423 bug 2628 eindeutige meldung beim abtreiben 2020-09-25 18:20:45 +02:00
Enno Rehling c2754687dc
Merge pull request #923 from eressea/2706-lighthouse-drifting
2706 lighthouses and drifting
2020-09-25 17:39:26 +02:00
Enno Rehling 43a0368fb4 lighthouses protect from drifting
do not check for maintenance in update_lighthouse, but when the lighthouse is used.
2020-09-24 20:12:51 +02:00
Enno Rehling 65c05f37d2 ships on a ROUTE can drift. 2020-09-24 20:11:45 +02:00
Enno Rehling d7d3b5d730 Merge branch 'master' into develop 2020-09-24 19:30:16 +02:00
Enno Rehling 912ce0f382 Regentanz / Segen der Erde
Der Effekt soll angeblich nur für Bauern gelten, nicht für Spieler.
2020-09-19 03:59:26 +02:00
Enno Rehling 44ac1d5573 Refactor the wage function for readability.
Add a test for wages at different castle sizes.
2020-09-16 19:04:58 +02:00
Enno Rehling 7af2735b7f screw you, iwyu 2020-09-14 12:37:03 +02:00
Enno Rehling 7712dcc0f0 move gcc-specific signal handling out of main.c 2020-09-13 20:34:45 +02:00
Enno Rehling b988dd0a7d pointer out of scope 2020-09-13 19:55:10 +02:00
Enno Rehling 0165dbf759 Zauber, revisited.
Mauern der Ewigkeit und Störe Astrale Integrität brauchen keine Stufenangabe, ihre Kosten sind nicht variabel
2020-09-13 19:48:43 +02:00
Enno Rehling a016ea9a14 Änderung an Heiliger Boden war ein Fehler. 2020-09-13 15:42:01 +02:00
Enno Rehling 1f1125f9fd https://bugs.eressea.de/view.php?id=2651
Materialkosten von Heiliger Boden, Mauern der Ewigkeit, Störe Astrale Integrität steigen mit der Stude des Zaubers.
2020-09-13 15:26:41 +02:00
Enno Rehling e61b8ad557 update CHANGELOG 2020-09-13 14:33:13 +02:00
Enno Rehling 57b5e50db8 resource visibility, code clarification. 2020-09-13 14:33:13 +02:00
Enno Rehling 7c44ff2241 add a setting for an alternate resource discovery rule. 2020-09-13 14:33:13 +02:00
Enno Rehling b7aad91ffb division by zero error 2020-09-13 14:33:13 +02:00
Enno Rehling 85f1207af0 teachers do not need to be in an academy.
remove source files for academy.
2020-09-13 14:33:13 +02:00
Enno Rehling c884138351 coverity complains about possibly missing warden. 2020-09-13 14:33:13 +02:00
Enno Rehling 039d3ecc6b start version 3.26 development Q4/2020 2020-09-13 14:33:13 +02:00
Enno Rehling 1c8248ee0c https://bugs.eressea.de/view.php?id=2701
fix the academy
make academy, curses and potions work for LERNE AUTO
remove E3 study.speedup
2020-09-13 14:33:13 +02:00
50 changed files with 614 additions and 592 deletions

View File

@ -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.

View File

@ -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"
]
}

View File

@ -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,

View File

@ -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>

View File

@ -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>

View File

@ -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.\""

View File

@ -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.\""

View File

@ -34,8 +34,9 @@ function test_laen_needs_mine()
assert_equal(1, f:count_msg_type("building_needed")) -- requires building
u.building = building.create(u.region, "mine")
u.building.working = true
u.building.size = 10
u:add_item('money', 500) -- Unterhalt Bergwerk
u.building.working = true
turn_process()
assert_equal(1, u:get_item('laen'))
assert_equal(99, r:get_resource('laen'))
@ -56,15 +57,19 @@ function test_mine_laen_bonus()
u:add_order("MACHE Laen")
u:set_skill('mining', 6)
u.building = building.create(u.region, "mine")
u.building.working = true
u.building.size = 10
u.number = 2
u:add_item('money', 500) -- Unterhalt Bergwerk
u.building.working = true
turn_process() -- T6 is not enough for laen
assert_equal(0, u:get_item('laen'))
assert_equal(100, r:get_resource('laen'))
assert_equal(1, f:count_msg_type("manufacture_skills"))
u:set_skill('mining', 13)
u:add_item('money', 500) -- Unterhalt Bergwerk
u.building.working = true
turn_process() -- T13 is enough, the +1 produces one extra Laen
assert_equal(4, u:get_item('laen')) -- FAIL (3)
assert_equal(96, r:get_resource('laen'))
@ -86,13 +91,14 @@ function test_mine_iron_bonus()
u:add_order("MACHE Eisen")
u:set_skill('mining', 1)
u.building = building.create(u.region, "mine")
u.building.working = false
u.building.size = 10
u.number = 2
turn_process() -- iron can be made without a working mine
assert_equal(2, u:get_item('iron'))
assert_equal(98, r:get_resource('iron'))
u:add_item('money', 500) -- Unterhalt Bergwerk
u.building.working = true
turn_process()
assert_equal(6, u:get_item('iron'))
@ -115,12 +121,13 @@ function test_quarry_bonus()
u:set_skill('quarrying', 1)
u.number = 2
u.building = building.create(u.region, 'quarry')
u.building.working = false
u.building.size = 10
turn_process()
assert_equal(2, u:get_item('stone'))
assert_equal(98, r:get_resource('stone'))
u:add_item('money', 250) -- Unterhalt Steinbruch
u.building.working = true
turn_process()
assert_equal(6, u:get_item('stone'))
@ -138,16 +145,18 @@ function test_smithy_no_bonus()
turn_begin()
u.building = building.create(u.region, 'smithy')
u.building.working = false
u.building.size = 10
u.number = 5
u:set_skill('cartmaking', 1) -- needs 1 min
u:add_item('log', 100)
u:add_order("MACHE Wagen")
turn_process() -- building disabled
turn_process() -- building disabled, money is missing
assert_equal(5, u:get_item('cart'))
assert_equal(75, u:get_item('log'))
u:add_item('money', 300) -- Unterhalt Schmiede
u:add_item('log', 1)
u.building.working = true
turn_process() -- building active
assert_equal(10, u:get_item('cart'))
@ -164,7 +173,7 @@ function test_smithy_bonus_iron()
turn_begin()
u.building = building.create(u.region, 'smithy')
u.building.working = false
u.building.size = 10
u:set_skill('weaponsmithing', 5) -- needs 3
u:add_item('iron', 100)
@ -173,6 +182,8 @@ function test_smithy_bonus_iron()
assert_equal(1, u:get_item('sword'))
assert_equal(99, u:get_item('iron'))
u:add_item('log', 1) -- Unterhalt Schmiede
u:add_item('money', 300) -- Unterhalt Schmiede
u.building.working = true
turn_process() -- building active
assert_equal(3, u:get_item('sword'))
@ -190,7 +201,7 @@ function test_smithy_bonus_mixed()
turn_begin()
u.building = building.create(u.region, 'smithy')
u.building.working = false
u.building.size = 10
u:set_skill('weaponsmithing', 5) -- needs 3
u:add_item('iron', 100)
@ -201,6 +212,8 @@ function test_smithy_bonus_mixed()
assert_equal(99, u:get_item('iron'))
assert_equal(99, u:get_item('log'))
u:add_item('money', 300) -- Unterhalt Schmiede
u:add_item('log', 1) -- Unterhalt Schmiede
u.building.working = true
turn_process() -- building active
assert_equal(3, u:get_item('axe'))

View File

@ -97,7 +97,7 @@ end
function test_lighthouse()
eressea.free_game()
local r = region.create(0, 0, "mountain")
local f = faction.create("human", "human@example.com")
local f = faction.create("human", "lighthouse@example.com")
local f2 = faction.create("dwarf")
local r2 = region.create(1, 0, "mountain")
unit.create(f2, r2, 1).name = 'The Babadook'
@ -108,7 +108,6 @@ function test_lighthouse()
local u = unit.create(f, r, 1)
local b = building.create(r, "lighthouse")
b.size = 100
b.working = true
u.building = b
u:set_skill("perception", 9)
u:add_item("money", 1000)

View File

@ -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
)
@ -180,7 +180,9 @@ set (SERVER_SRC ${SERVER_SRC}
endif(CURSES_FOUND)
find_program(IWYU_PATH NAMES include-what-you-use iwyu)
if(NOT IWYU_PATH)
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

View File

@ -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));
}

View File

@ -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

View File

@ -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;
}

View File

@ -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));
}
}
}
}

View File

@ -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);
}
}

View File

@ -39,15 +39,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_MAINTAINED;
else self->flags &= ~BLD_MAINTAINED;
return 1;
if (flag) self->flags &= ~BLD_UNMAINTAINED;
else self->flags |= BLD_UNMAINTAINED;
return 0;
}
static int tolua_building_get_working(lua_State * L)
{
building *self = (building *)tolua_tousertype(L, 1, 0);
bool flag = (self->flags&BLD_MAINTAINED) != 0;
bool flag = (self->flags & BLD_UNMAINTAINED) == 0;
lua_pushboolean(L, flag);
return 1;
}

View File

@ -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);

View File

@ -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);
}
}

View File

@ -305,31 +305,34 @@ static int forget_cmd(unit * u, order * ord)
return 0;
}
static int maintain(building * b)
static bool maintain(building * b)
{
int c;
region *r = b->region;
bool paid = true;
unit *u;
if (fval(b, BLD_MAINTAINED) || b->type == NULL || b->type->maintenance == NULL) {
return BLD_MAINTAINED;
}
if (fval(b, BLD_DONTPAY)) {
return 0;
if (b->type == NULL || b->type->maintenance == NULL) {
return true;
}
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 0;
return false;
}
/* 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 0;
/* If the owner is the region owner, check if dontpay flag is set for the building he is in */
if (b != u->building) {
if (!config_token("rules.region_owner_pay_building", b->type->_name)) {
/* no owner - send a message to the entire region */
ADDMSG(&r->msgs, msg_message("maintenance_noowner", "building", b));
return false;
}
}
if (fval(u->building, BLD_DONTPAY)) {
ADDMSG(&r->msgs, msg_message("maintenance_nowork", "building", b));
return false;
}
for (c = 0; b->type->maintenance[c].number && paid; ++c) {
const maintenance *m = b->type->maintenance + c;
int need = m->number;
@ -341,14 +344,10 @@ static int maintain(building * b)
paid = false;
}
}
if (fval(b, BLD_DONTPAY)) {
ADDMSG(&r->msgs, msg_message("maintenance_nowork", "building", b));
return 0;
}
if (!paid) {
ADDMSG(&u->faction->msgs, msg_message("maintenancefail", "unit building", u, b));
ADDMSG(&r->msgs, msg_message("maintenance_nowork", "building", b));
return 0;
return paid;
}
for (c = 0; b->type->maintenance[c].number; ++c) {
const maintenance *m = b->type->maintenance + c;
@ -363,7 +362,7 @@ static int maintain(building * b)
assert(cost == 0);
}
ADDMSG(&u->faction->msgs, msg_message("maintenance", "unit building", u, b));
return BLD_MAINTAINED;
return true;
}
void maintain_buildings(region * r)
@ -371,12 +370,11 @@ void maintain_buildings(region * r)
building **bp = &r->buildings;
while (*bp) {
building *b = *bp;
int flags = BLD_MAINTAINED;
if (!curse_active(get_curse(b->attribs, &ct_nocostbuilding))) {
flags = maintain(b);
if (!maintain(b)) {
fset(b, BLD_UNMAINTAINED);
}
}
fset(b, flags);
bp = &b->next;
}
}
@ -1993,7 +1991,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 +2016,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 +2060,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;

View File

@ -497,7 +497,7 @@ static void test_maintain_buildings(CuTest *tc) {
/* this building has no upkeep, it just works: */
b->flags = 0;
maintain_buildings(r);
CuAssertIntEquals(tc, BLD_MAINTAINED, fval(b, BLD_MAINTAINED));
CuAssertIntEquals(tc, 0, fval(b, BLD_UNMAINTAINED));
CuAssertPtrEquals(tc, NULL, f->msgs);
CuAssertPtrEquals(tc, NULL, r->msgs);
@ -509,7 +509,7 @@ static void test_maintain_buildings(CuTest *tc) {
/* we cannot afford to pay: */
b->flags = 0;
maintain_buildings(r);
CuAssertIntEquals(tc, 0, fval(b, BLD_MAINTAINED));
CuAssertIntEquals(tc, BLD_UNMAINTAINED, fval(b, BLD_UNMAINTAINED));
CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "maintenancefail"));
CuAssertPtrNotNull(tc, test_find_messagetype(r->msgs, "maintenance_nowork"));
test_clear_messagelist(&f->msgs);
@ -519,7 +519,7 @@ static void test_maintain_buildings(CuTest *tc) {
i_change(&u->items, itype, 100);
b->flags = 0;
maintain_buildings(r);
CuAssertIntEquals(tc, BLD_MAINTAINED, fval(b, BLD_MAINTAINED));
CuAssertIntEquals(tc, 0, fval(b, BLD_UNMAINTAINED));
CuAssertIntEquals(tc, 0, i_get(u->items, itype));
CuAssertPtrEquals(tc, NULL, r->msgs);
CuAssertPtrEquals(tc, NULL, test_find_messagetype(f->msgs, "maintenance_nowork"));
@ -530,7 +530,7 @@ static void test_maintain_buildings(CuTest *tc) {
u_set_building(u, NULL);
b->flags = 0;
maintain_buildings(r);
CuAssertIntEquals(tc, 0, fval(b, BLD_MAINTAINED));
CuAssertIntEquals(tc, BLD_UNMAINTAINED, fval(b, BLD_UNMAINTAINED));
CuAssertPtrEquals(tc, NULL, f->msgs);
CuAssertPtrNotNull(tc, test_find_messagetype(r->msgs, "maintenance_noowner"));
test_clear_messagelist(&r->msgs);

View File

@ -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;
}
}

View File

@ -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;

View File

@ -829,7 +829,6 @@ build_building(unit * u, const building_type * btype, int id, int want, order *
/* build a new building */
b = new_building(btype, r, lang, built);
b->type = btype;
fset(b, BLD_MAINTAINED);
/* Die Einheit befindet sich automatisch im Inneren der neuen Burg. */
if (u->number && leave(u, false)) {
@ -879,10 +878,6 @@ build_building(unit * u, const building_type * btype, int id, int want, order *
}
fset(b, BLD_EXPANDED);
if (is_lighthouse(btype)) {
update_lighthouse(b);
}
return built;
}

View File

@ -404,9 +404,6 @@ building *new_building(const struct building_type * btype, region * r,
bptr = &(*bptr)->next;
*bptr = b;
if (is_lighthouse(b->type)) {
update_lighthouse(b);
}
bname = LOC(lang, btype->_name);
if (!bname) {
bname = LOC(lang, parameters[P_GEBAEUDE]);
@ -445,9 +442,6 @@ void remove_building(building ** blist, building * b)
if (u->building == b) leave(u, true);
}
if (is_lighthouse(b->type)) {
remove_lighthouse(b);
}
b->size = 0;
bunhash(b);
@ -495,20 +489,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 +514,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 {
@ -637,7 +633,7 @@ 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 && (!working || fval(b, BLD_MAINTAINED)) && building_finished(b)) {
if (b->type == bt && !(working && fval(b, BLD_UNMAINTAINED)) && building_finished(b)) {
return true;
}
}
@ -650,7 +646,7 @@ bool building_finished(const struct building *b) {
}
bool building_is_active(const struct building *b) {
return b && fval(b, BLD_MAINTAINED) && building_finished(b);
return b && !fval(b, BLD_UNMAINTAINED) && building_finished(b);
}
building *active_building(const unit *u, const struct building_type *btype) {
@ -728,7 +724,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 +735,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 +760,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 +780,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)

View File

@ -79,7 +79,7 @@ extern "C" {
bool in_safe_building(struct unit *u1, struct unit *u2);
#define BFL_NONE 0x00
#define BLD_MAINTAINED 0x01 /* vital maintenance paid for */
#define BLD_UNMAINTAINED 0x01 /* vital maintenance not paid for */
#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 */
@ -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);

View File

@ -332,17 +332,16 @@ static void test_buildingtype_exists(CuTest * tc)
CuAssertTrue(tc, buildingtype_exists(r, btype, false));
b->size = 9;
fset(b, BLD_MAINTAINED);
CuAssertTrue(tc, !buildingtype_exists(r, btype, false));
btype->maxsize = 0;
freset(b, BLD_MAINTAINED);
fset(b, BLD_UNMAINTAINED);
CuAssertTrue(tc, buildingtype_exists(r, btype, false));
btype->maxsize = 10;
b->size = 10;
fset(b, BLD_MAINTAINED);
freset(b, BLD_UNMAINTAINED);
CuAssertTrue(tc, buildingtype_exists(r, btype, true));
freset(b, BLD_MAINTAINED);
fset(b, BLD_UNMAINTAINED);
CuAssertTrue(tc, !buildingtype_exists(r, btype, true));
test_teardown();
}
@ -359,10 +358,7 @@ static void test_active_building(CuTest *tc) {
assert(btype && btype->maxsize == -1);
b = test_create_building(r = test_create_region(0, 0, NULL), btype);
u = test_create_unit(test_create_faction(NULL), r);
CuAssertIntEquals(tc, false, building_is_active(b));
CuAssertPtrEquals(tc, NULL, active_building(u, btype));
b->flags |= BLD_MAINTAINED;
CuAssertIntEquals(tc, true, building_is_active(b));
CuAssertPtrEquals(tc, NULL, active_building(u, btype));
u_set_building(u, b);
@ -376,7 +372,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_MAINTAINED;
b->flags |= BLD_UNMAINTAINED;
CuAssertIntEquals(tc, false, building_is_active(b));
CuAssertPtrEquals(tc, NULL, active_building(u, btype));
test_teardown();
@ -434,6 +430,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 +689,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);

View File

@ -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

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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;
}

View File

@ -1563,18 +1563,6 @@ static void read_regions(gamedata *data) {
}
}
}
log_debug("updating area information for lighthouses.");
for (r = regions; r; r = r->next) {
if (r->flags & RF_LIGHTHOUSE) {
building *b;
for (b = r->buildings; b; b = b->next) {
if (is_lighthouse(b->type)) {
update_lighthouse(b);
}
}
}
}
}
static void init_factions(int data_version)

View File

@ -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) {

View File

@ -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) {

View File

@ -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
@ -54,8 +59,9 @@ void update_lighthouse(building * lh)
a = a_find(r2->attribs, &at_lighthouse);
while (a && a->type == &at_lighthouse) {
building *b = (building *)a->data.v;
if (b == lh)
if (b == lh) {
break;
}
a = a->next;
}
if (!a) {
@ -67,21 +73,22 @@ void update_lighthouse(building * lh)
}
}
void remove_lighthouse(const building *lh) {
building *b;
region * r = lh->region;
r->flags &= ~RF_LIGHTHOUSE;
for (b = r->buildings; b; b = b->next) {
if (b != lh && is_lighthouse(b->type)) {
update_lighthouse(b);
bool update_lighthouses(region *r) {
if ((r->flags & RF_LIGHTHOUSE) == 0) {
building *b;
for (b = r->buildings; b; b = b->next) {
if (is_lighthouse(b->type)) {
update_lighthouse(b);
}
}
return true;
}
return false;
}
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;
@ -89,7 +96,7 @@ int lighthouse_range(const building * b)
int lighthouse_view_distance(const building * b, const unit *u)
{
if (b->size >= 10 && (b->flags & BLD_MAINTAINED)) {
if (b->size >= 10 && building_is_active(b)) {
int maxd = lighthouse_range(b);
if (maxd > 0 && u && skill_enabled(SK_PERCEPTION)) {
@ -112,13 +119,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 (building_is_active(b)) {
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;
}
}
}
}
}

View File

@ -20,7 +20,7 @@ extern "C" {
bool is_lighthouse(const struct building_type *btype);
bool lighthouse_guarded(const struct region *r);
void update_lighthouse(struct building *b);
void remove_lighthouse(const struct building *lh);
bool update_lighthouses(struct region *r);
int lighthouse_range(const struct building *b);
int lighthouse_view_distance(const struct building *b, const struct unit *u);

View File

@ -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;
@ -53,6 +51,9 @@ static void test_lighthouse_range(CuTest * tc)
b->size = 99;
CuAssertIntEquals(tc, 2, lighthouse_view_distance(b, u1));
b->flags |= BLD_UNMAINTAINED;
CuAssertIntEquals(tc, 0, lighthouse_view_distance(b, u1));
test_teardown();
}
@ -70,7 +71,7 @@ static void test_lighthouse_update(CuTest * tc)
r3 = test_create_region(2, 0, t_ocean);
r4 = test_create_region(0, 1, t_plain);
b = test_create_building(r1, test_create_buildingtype("lighthouse"));
b->flags |= BLD_MAINTAINED;
update_lighthouse(b);
CuAssertIntEquals(tc, RF_LIGHTHOUSE, r1->flags&RF_LIGHTHOUSE);
CuAssertPtrEquals(tc, NULL, r1->attribs);
CuAssertPtrEquals(tc, NULL, r2->attribs);
@ -112,7 +113,6 @@ static void test_lighthouse_guard(CuTest * tc) {
r3 = test_create_region(2, 0, t_ocean);
r4 = test_create_region(0, 1, t_plain);
b = test_create_building(r1, test_create_buildingtype("lighthouse"));
b->flags |= BLD_MAINTAINED;
b->size = 10;
CuAssertIntEquals(tc, 2, lighthouse_range(b));
update_lighthouse(b);
@ -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();

View File

@ -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, "");

View File

@ -65,7 +65,6 @@ static void test_market_curse(CuTest * tc)
}
r = findregion(1, 1);
b = test_create_building(r, btype);
b->flags |= BLD_MAINTAINED;
b->size = b->type->maxsize;
f = test_create_faction(NULL);

View File

@ -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;
}

View File

@ -1157,12 +1157,19 @@ static bool can_move(const unit * u)
return true;
}
static void init_transportation(void)
static void init_movement(void)
{
region *r;
for (r = regions; r; r = r->next) {
unit *u;
building *b;
for (b = r->buildings; b; b = b->next) {
if (is_lighthouse(b->type)) {
update_lighthouse(b);
}
}
/* This is just a simple check for non-corresponding K_TRANSPORT/
* K_DRIVE. This is time consuming for an error check, but there
@ -1612,9 +1619,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 +2199,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);
@ -2409,8 +2416,8 @@ void movement(void)
{
int ships;
/* Initialize the additional encumbrance by transported units */
init_transportation();
/* Initialize lighthouses and additional encumbrance by transported units */
init_movement();
/* Move ships in last phase, others first
* This is to make sure you can't land someplace and then get off the ship

View File

@ -84,7 +84,6 @@ static void setup_harbor(move_fixture *mf) {
r = test_create_region(0, 0, ttype);
b = test_create_building(r, btype);
b->flags |= BLD_MAINTAINED;
u = test_create_unit(test_create_faction(NULL), r);
u->ship = sh;

View File

@ -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);

View File

@ -1652,9 +1652,13 @@ static void check_messages_exist(void) {
int init_reports(void)
{
region *r;
bool update = true;
check_messages_exist();
create_directories();
for (r = regions; r; r = r->next) {
if (update) {
update = update_lighthouses(r);
}
reorder_units(r);
}
return 0;

View File

@ -503,7 +503,6 @@ void test_prepare_lighthouse_capacity(CuTest *tc) {
r1 = test_create_region(0, 0, t_plain);
r2 = test_create_region(1, 0, t_ocean);
b = test_create_building(r1, btype);
b->flags |= BLD_MAINTAINED;
b->size = 10;
update_lighthouse(b);
u1 = test_create_unit(test_create_faction(NULL), r1);
@ -561,7 +560,6 @@ static void test_prepare_lighthouse(CuTest *tc) {
r4 = test_create_region(0, 1, t_plain);
btype = test_create_buildingtype("lighthouse");
b = test_create_building(r1, btype);
b->flags |= BLD_MAINTAINED;
b->size = 10;
update_lighthouse(b);
u = test_create_unit(f, r1);
@ -605,7 +603,6 @@ static void test_prepare_lighthouse_owners(CuTest *tc)
r3 = test_create_region(3, 0, t_ocean);
btype = test_create_buildingtype("lighthouse");
b = test_create_building(r1, btype);
b->flags |= BLD_MAINTAINED;
b->size = 10;
update_lighthouse(b);
test_create_unit(f, r1);

35
src/signals.c Normal file
View File

@ -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

4
src/signals.h Normal file
View File

@ -0,0 +1,4 @@
#pragma once
int setup_signal_handler(void);

View File

@ -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);
}
}
}
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;
}
}
}
}
}
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;
}
}
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 (is_cursed(r->attribs, &ct_badlearn)) {
days -= EXPERIENCEDAYS * u->number;
}
if (fval(u, UFL_HUNGER)) {
days /= 2;
}
assert(sk >= 0 && sk < MAXSKILLS);
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);
}
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;

View File

@ -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);

View File

@ -204,7 +204,6 @@ static void test_study_bug_2194(CuTest *tc) {
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();
@ -280,7 +279,6 @@ static void test_academy_building(CuTest *tc) {
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();
@ -335,7 +333,6 @@ static void test_academy_bonus(CuTest *tc) {
scale_number(u1, 9);
scale_number(u3, 2);
i_change(&u1->items, get_resourcetype(R_SILVER)->itype, 5000);
b->flags = BLD_MAINTAINED;
learn_inject();
teach_cmd(u0, u0->thisorder);
@ -364,14 +361,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 +382,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();

View File

@ -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);