From fb21d6d9323100b6552103adae0c851abbd56cbb Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 4 Jan 2020 12:22:14 +0100 Subject: [PATCH 01/14] kill ancient godcursezone code. add some tests for planting seeds. --- res/core/messages.xml | 15 -------- res/translations/messages.de.po | 9 ----- res/translations/messages.en.po | 9 ----- res/translations/strings.de.po | 4 -- res/translations/strings.en.po | 4 -- scripts/tests/e2/init.lua | 3 ++ scripts/tests/e2/trees.lua | 66 +++++++++++++++++++++++++++++++++ src/bindings.c | 2 +- src/kernel/building.c | 4 -- src/kernel/curse.c | 1 + src/laws.c | 54 +++++++++------------------ src/randenc.c | 39 ------------------- src/spells/regioncurse.c | 25 ------------- src/spells/regioncurse.h | 1 - 14 files changed, 88 insertions(+), 148 deletions(-) create mode 100644 scripts/tests/e2/trees.lua diff --git a/res/core/messages.xml b/res/core/messages.xml index ecb5707c1..75d23a04b 100644 --- a/res/core/messages.xml +++ b/res/core/messages.xml @@ -391,16 +391,6 @@ - - - - - - - - - - @@ -712,11 +702,6 @@ - - - - - diff --git a/res/translations/messages.de.po b/res/translations/messages.de.po index af60271ba..6ad2c1d6b 100644 --- a/res/translations/messages.de.po +++ b/res/translations/messages.de.po @@ -437,9 +437,6 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - Pferde kann man msgid "error307" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Snotlinge sind zu dumm, um auf den Feldern zu arbeiten.\"" -msgid "godcurse_destroy_ship" -msgstr "\"Die Mannschaft krank vom vergifteten Wasser, Planken, Ruder und Segel zerfressen von den Wassern des verfluchten Meeres, ergibt sich die $ship($ship) in ihr Schicksal und sinkt.\"" - msgid "too_many_units_in_faction" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Eine Partei darf nicht aus mehr als $int($allowed) Einheiten bestehen.\"" @@ -1034,9 +1031,6 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - Auf dem Schiff msgid "sp_icastle_effect" msgstr "\"Verwundert blicken die Bauern von $region($region) auf ein neues Gebäude.\"" -msgid "curseinfo::godcurse" -msgstr "\"Diese Region wurde von den Göttern verflucht. Stinkende Nebel ziehen über die tote Erde und furchtbare Kreaturen ziehen über das Land. Die Brunnen sind vergiftet, und die wenigen essbaren Früchte sind von einem rosa Pilz überzogen. Niemand kann hier lange überleben. ($int36($id))\"" - msgid "recruit_archetype" msgstr "\"$unit($unit) rekrutiert $int($amount) $localize($archetype).\"" @@ -2555,9 +2549,6 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - Man benötigt m msgid "error177" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Diesen Spruch kann der Vertraute nicht zaubern.\"" -msgid "curseinfo::godcurseocean" -msgstr "\"Diese Region wurde von den Göttern verflucht. Das Meer ist eine ekelige Brühe, braunschwarze, stinkende Gase steigen aus den unergründlichen Tiefen hervor, und untote Seeungeheuer, Schiffe zerfressend und giftige grüne Galle geifernd, sind der Schrecken aller Seeleute, die diese Gewässer durchqueren. Niemand kann hier lange überleben. ($int36($id))\"" - msgid "curseinfo::sparkle_4" msgstr "\"Ein schimmernder Lichterkranz umgibt $unit($unit). ($int36($id))\"" diff --git a/res/translations/messages.en.po b/res/translations/messages.en.po index 9ec9c934b..3f7106f11 100644 --- a/res/translations/messages.en.po +++ b/res/translations/messages.en.po @@ -437,9 +437,6 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - You can only br msgid "error307" msgstr "\"$unit($unit) in $region($region): '$order($command)' - We snotlings is too stupid fer dat!\"" -msgid "godcurse_destroy_ship" -msgstr "\"Her sailors sick from the poisened ocean, planks, rudder und sails corroded by the waters of the cursed ocean, the $ship($ship) finally succumbs to her destiny and sinks.\"" - msgid "too_many_units_in_faction" msgstr "\"$unit($unit) in $region($region): '$order($command)' - A faction may not consist of more than $int($allowed) units.\"" @@ -1034,9 +1031,6 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - There are not e msgid "sp_icastle_effect" msgstr "\"Flabbergasted, the peasants of $region($region) behold a new building.\"" -msgid "curseinfo::godcurse" -msgstr "\"This region was cursed by the gods. Stinking vapors billow over the dead ground and hideous creatures move about the country. The wells are poisened and the edible plants are covered by a pink fungus. Noone can live here for long. ($int36($id))\"" - msgid "recruit_archetype" msgstr "\"$unit($unit) recruits $int($amount) $localize($archetype).\"" @@ -2555,9 +2549,6 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - You need at lea msgid "error177" msgstr "\"$unit($unit) in $region($region): '$order($command)' - The familiar cannot cast this spell.\"" -msgid "curseinfo::godcurseocean" -msgstr "\"This region was cursed by the gods. The sea is a foul cesspool, noxious gases rise from the deep, undead seamonsters attack all ships. Noone can live here for long. ($int36($id))\"" - msgid "curseinfo::sparkle_4" msgstr "\"A circle of shimmering lights surrounds $unit($unit). ($int36($id))\"" diff --git a/res/translations/strings.de.po b/res/translations/strings.de.po index 1351c3d05..0e9776a40 100644 --- a/res/translations/strings.de.po +++ b/res/translations/strings.de.po @@ -2331,10 +2331,6 @@ msgstr "Seelenfrieden" msgid "list_and" msgstr " und " -msgctxt "spell" -msgid "godcursezone" -msgstr "Fluch der Götter" - msgid "sptype_postcombat" msgstr "Postkampfzauber" diff --git a/res/translations/strings.en.po b/res/translations/strings.en.po index 0eb8d3a37..733ccfd56 100644 --- a/res/translations/strings.en.po +++ b/res/translations/strings.en.po @@ -1976,10 +1976,6 @@ msgstr " and " msgid "h18_p" msgstr "ice begonias" -msgctxt "spell" -msgid "godcursezone" -msgstr "Curse of the Gods" - msgid "sptype_postcombat" msgstr "post-combat spell" diff --git a/scripts/tests/e2/init.lua b/scripts/tests/e2/init.lua index 0f377a991..e3e007ff0 100644 --- a/scripts/tests/e2/init.lua +++ b/scripts/tests/e2/init.lua @@ -1,3 +1,6 @@ +require 'tests.e2.trees' +require 'tests.e2.allies' +require 'tests.e2.quit' require 'tests.e2.movement' require 'tests.e2.carts' require 'tests.e2.quit' diff --git a/scripts/tests/e2/trees.lua b/scripts/tests/e2/trees.lua new file mode 100644 index 000000000..857d528ff --- /dev/null +++ b/scripts/tests/e2/trees.lua @@ -0,0 +1,66 @@ +require "lunit" + +module("tests.e2.trees", package.seeall, lunit.testcase ) + +function setup() + eressea.game.reset() + eressea.settings.set("rules.food.flags", "4") -- food is free + eressea.settings.set("NewbieImmunity", "0") +end + +function test_no_growth() + set_turn(204) + assert_equal('spring', get_season()) + local r = region.create(0, 0, 'plain') + r:set_resource('seed', 0) + r:set_resource('sapling', 0) + r:set_resource('tree', 0) + process_orders() + assert_equal(0, r:get_resource('seed')) + assert_equal(0, r:get_resource('sapling')) + assert_equal(0, r:get_resource('tree')) +end + +function x_test_plant_seeds() + set_turn(184) + assert_equal('summer', get_season()) + local f = faction.create('goblin') + local r = region.create(0, 0, 'plain') + r:set_resource('seed', 0) + r:set_resource('sapling', 0) + r:set_resource('tree', 0) + local u = unit.create(f, r) + u:set_skill('herbalism', 20) + u:add_item('seed', 40) + u:add_order("PFLANZE 20 Samen") + process_orders() + assert_equal(0, r:get_resource('sapling')) + assert_equal(20, r:get_resource('seed')) + assert_equal(0, r:get_resource('tree')) + assert_equal(20, u:get_item('seed')) +end + +function test_plant_seeds_spring() + set_turn(202) + assert_equal('spring', get_season()) + local f = faction.create('goblin') + local r = region.create(0, 0, 'plain') + r:set_resource('seed', 0) + r:set_resource('sapling', 0) + r:set_resource('tree', 0) + + process_orders() + assert_equal(0, r:get_resource('sapling')) + assert_equal(0, r:get_resource('seed')) + assert_equal(0, r:get_resource('tree')) + + local u = unit.create(f, r) + u:set_skill('herbalism', 20) + u:add_item('seed', 40) + u:add_order("PFLANZE 20 Samen") + process_orders() + assert_equal(20, r:get_resource('sapling')) -- T12+ in Spring + assert_equal(0, r:get_resource('seed')) + assert_equal(0, r:get_resource('tree')) + assert_equal(20, u:get_item('seed')) +end diff --git a/src/bindings.c b/src/bindings.c index f2beb6d06..6d2481a29 100755 --- a/src/bindings.c +++ b/src/bindings.c @@ -260,7 +260,7 @@ static int tolua_dice_rand(lua_State * L) static int tolua_get_season(lua_State * L) { - int turn_no = (int)tolua_tonumber(L, 1, 0); + int turn_no = (int)tolua_tonumber(L, 1, turn); season_t season = calendar_season(turn_no); tolua_pushstring(L, seasonnames[season]); return 1; diff --git a/src/kernel/building.c b/src/kernel/building.c index aba7953a0..a6167f880 100644 --- a/src/kernel/building.c +++ b/src/kernel/building.c @@ -746,10 +746,6 @@ default_wage(const region * r, const faction * f, const race * rc, int in_turn) variant vm; /* Godcurse: Income -10 */ - c = get_curse(r->attribs, &ct_godcursezone); - if (c && curse_active(c)) { - wage = (wage < 10) ? 0 : (wage - 10); - } vm = frac_make(wage, 1); /* Bei einer Duerre verdient man nur noch ein Viertel */ diff --git a/src/kernel/curse.c b/src/kernel/curse.c index f96e2d212..8e8df36ac 100644 --- a/src/kernel/curse.c +++ b/src/kernel/curse.c @@ -129,6 +129,7 @@ static int read_ccompat(const char *cursename, struct storage *store) } *seek, old_curses[] = { { "disorientationzone", "" }, { "shipdisorientation", "" }, + { "godcursezone", "" }, { NULL, NULL } }; for (seek = old_curses; seek->name; ++seek) { diff --git a/src/laws.c b/src/laws.c index 53b49e1b6..b065c3a31 100644 --- a/src/laws.c +++ b/src/laws.c @@ -463,10 +463,7 @@ static void horses(region * r) maxhorses = region_maxworkers(r) / 10; horses = rhorses(r); if (horses > 0) { - if (is_cursed(r->attribs, &ct_godcursezone)) { - rsethorses(r, (int)(horses * 0.9)); - } - else if (maxhorses > 0) { + if (maxhorses > 0) { double growth = (RESOURCE_QUANTITY * (HORSEGROWTH * 200.0 * ((double)maxhorses - horses))) / (double)maxhorses; @@ -625,12 +622,6 @@ growing_trees(region * r, const season_t current_season, const season_t last_wee a_removeall(&r->attribs, &at_germs); } - if (is_cursed(r->attribs, &ct_godcursezone)) { - rsettrees(r, 1, (int)(rtrees(r, 1) * 0.9)); - rsettrees(r, 2, (int)(rtrees(r, 2) * 0.9)); - return; - } - mp = max_production(r); if (mp <= 0) return; @@ -685,9 +676,6 @@ growing_trees(region * r, const season_t current_season, const season_t last_wee else if (current_season == SEASON_SPRING) { int growth; - if (is_cursed(r->attribs, &ct_godcursezone)) - return; - /* in at_germs merken uns die Zahl der Samen und Sproesslinge, die * dieses Jahr aelter werden duerfen, damit nicht ein Same im selben * Zyklus zum Baum werden kann */ @@ -701,31 +689,7 @@ growing_trees(region * r, const season_t current_season, const season_t last_wee * zu wachsen, damit sollten nach 5-6 Wochen alle gewachsen sein */ growth = 1800; - /* Samenwachstum */ - - /* Raubbau abfangen, es duerfen nie mehr Samen wachsen, als aktuell - * in der Region sind */ - seeds = rtrees(r, 0); - if (seeds > a->data.sa[0]) seeds = a->data.sa[0]; - sprout = 0; - - for (i = 0; i < seeds; i++) { - if (rng_int() % 10000 < growth) - sprout++; - } - /* aus dem Samenpool dieses Jahres abziehen */ - a->data.sa[0] = (short)(seeds - sprout); - /* aus dem gesamt Samenpool abziehen */ - rsettrees(r, 0, rtrees(r, 0) - sprout); - /* zu den Sproesslinge hinzufuegen */ - rsettrees(r, 1, rtrees(r, 1) + sprout); - /* Baumwachstum */ - - /* hier gehen wir davon aus, das Jungbaeume nicht ohne weiteres aus - * der Region entfernt werden koennen, da Jungbaeume in der gleichen - * Runde nachwachsen, wir also nicht mehr zwischen diesjaehrigen und - * 'alten' Jungbaeumen unterscheiden koennten */ sprout = rtrees(r, 1); if (sprout > a->data.sa[1]) sprout = a->data.sa[1]; grownup_trees = 0; @@ -740,6 +704,22 @@ growing_trees(region * r, const season_t current_season, const season_t last_wee rsettrees(r, 1, rtrees(r, 1) - grownup_trees); /* zu den Baeumen hinzufuegen */ rsettrees(r, 2, rtrees(r, 2) + grownup_trees); + + /* Samenwachstum */ + seeds = rtrees(r, 0); + if (seeds > a->data.sa[0]) seeds = a->data.sa[0]; + sprout = 0; + + for (i = 0; i < seeds; i++) { + if (rng_int() % 10000 < growth) + sprout++; + } + /* aus dem Samenpool dieses Jahres abziehen */ + a->data.sa[0] = (short)(seeds - sprout); + /* aus dem gesamt Samenpool abziehen */ + rsettrees(r, 0, rtrees(r, 0) - sprout); + /* zu den Sproesslinge hinzufuegen */ + rsettrees(r, 1, rtrees(r, 1) + sprout); } } diff --git a/src/randenc.c b/src/randenc.c index 356c9c695..5464f0063 100644 --- a/src/randenc.c +++ b/src/randenc.c @@ -380,44 +380,6 @@ static void create_icebergs(void) } } -static void godcurse(void) -{ - region *r; - - for (r = regions; r; r = r->next) { - if (is_cursed(r->attribs, &ct_godcursezone)) { - unit *u; - for (u = r->units; u; u = u->next) { - skill *sv = u->skills; - while (sv != u->skills + u->skill_size) { - int weeks = 1 + rng_int() % 3; - reduce_skill(u, sv, weeks); - ++sv; - } - } - if (fval(r->terrain, SEA_REGION)) { - ship *sh; - for (sh = r->ships; sh;) { - ship *shn = sh->next; - double dmg = config_get_flt("rules.ship.damage.godcurse", 0.1); - damage_ship(sh, dmg); - if (sh->damage >= sh->size * DAMAGE_SCALE) { - unit *uo = ship_owner(sh); - if (uo) { - ADDMSG(&uo->faction->msgs, - msg_message("godcurse_destroy_ship", "ship", sh)); - } - sink_ship(sh); - remove_ship(&sh->region->ships, sh); - } - sh = shn; - } - } - } - } - -} - /** handles the "orcish" curse that makes units grow like old orks * TODO: This would probably be better handled in an age-function for the curse, * but it's now being called by randomevents() @@ -535,7 +497,6 @@ void randomevents(void) for (r = regions; r; r = r->next) { drown(r); } - godcurse(); orc_growth(); demon_skillchanges(); if (volcano_module()) { diff --git a/src/spells/regioncurse.c b/src/spells/regioncurse.c index 48565789e..9006d9218 100644 --- a/src/spells/regioncurse.c +++ b/src/spells/regioncurse.c @@ -22,30 +22,6 @@ #include #include -/* - * godcursezone - */ -static message *cinfo_cursed_by_the_gods(const void *obj, objtype_t typ, - const curse * c, int self) -{ - region *r = (region *)obj; - - UNUSED_ARG(typ); - UNUSED_ARG(self); - assert(typ == TYP_REGION); - - if (r->terrain->flags & SEA_REGION) { - return msg_message("curseinfo::godcurseocean", "id", c->no); - } - return msg_message("curseinfo::godcurse", "id", c->no); -} - -const struct curse_type ct_godcursezone = { - "godcursezone", - CURSETYP_NORM, CURSE_IMMUNE, (NO_MERGE), - cinfo_cursed_by_the_gods, -}; - /* --------------------------------------------------------------------- */ /* * C_GBDREAM @@ -299,7 +275,6 @@ void register_regioncurse(void) ct_register(&ct_badmagicresistancezone); ct_register(&ct_goodmagicresistancezone); ct_register(&ct_riotzone); - ct_register(&ct_godcursezone); ct_register(&ct_holyground); ct_register(&ct_healing); } diff --git a/src/spells/regioncurse.h b/src/spells/regioncurse.h index b1425daf0..7d11e6348 100644 --- a/src/spells/regioncurse.h +++ b/src/spells/regioncurse.h @@ -10,7 +10,6 @@ extern "C" { extern const struct curse_type ct_peacezone; extern const struct curse_type ct_drought; extern const struct curse_type ct_blessedharvest; - extern const struct curse_type ct_godcursezone; extern const struct curse_type ct_gbdream; extern const struct curse_type ct_healing; extern const struct curse_type ct_antimagiczone; From 967cfd107b805127353f97390eb87b928244d3d4 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 4 Jan 2020 20:00:57 +0100 Subject: [PATCH 02/14] re-enable test --- scripts/tests/e2/trees.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tests/e2/trees.lua b/scripts/tests/e2/trees.lua index 857d528ff..8ed5a5529 100644 --- a/scripts/tests/e2/trees.lua +++ b/scripts/tests/e2/trees.lua @@ -21,7 +21,7 @@ function test_no_growth() assert_equal(0, r:get_resource('tree')) end -function x_test_plant_seeds() +function test_plant_seeds() set_turn(184) assert_equal('summer', get_season()) local f = faction.create('goblin') From ea15af879e069d0cccee2183bae9685aa2b52038 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 4 Jan 2020 20:05:10 +0100 Subject: [PATCH 03/14] Bug 2625 disable springtime sapling test. --- scripts/tests/e2/trees.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tests/e2/trees.lua b/scripts/tests/e2/trees.lua index 8ed5a5529..9ead37099 100644 --- a/scripts/tests/e2/trees.lua +++ b/scripts/tests/e2/trees.lua @@ -40,7 +40,7 @@ function test_plant_seeds() assert_equal(20, u:get_item('seed')) end -function test_plant_seeds_spring() +function disable_test_plant_seeds_spring() set_turn(202) assert_equal('spring', get_season()) local f = faction.create('goblin') From 2e054f27ccab812bb0e1d28512d36f6b3a21bae0 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 7 Jan 2020 19:24:10 +0100 Subject: [PATCH 04/14] =?UTF-8?q?Bug=202630:=20Drachen=201.=20Jungdrachen?= =?UTF-8?q?=20sind=20Drachen=20(RCF=5FDRAGON)=202.=20Ein=20Monster=20das?= =?UTF-8?q?=20nicht=20k=C3=A4mpfen=20kann,=20braucht=20nicht=20zu=20pl?= =?UTF-8?q?=C3=BCndern.=203.=20make=20code=20more=20readable.=204.=20Wer?= =?UTF-8?q?=20schon=20bewacht,=20braucht=20keinen=20BEWACHE=20Befehl.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- res/eressea/races.xml | 2 +- scripts/run-turn.lua | 8 +++++--- src/monsters.c | 24 +++++++++++++----------- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/res/eressea/races.xml b/res/eressea/races.xml index 82b642ba2..8c3822110 100644 --- a/res/eressea/races.xml +++ b/res/eressea/races.xml @@ -721,7 +721,7 @@ - + diff --git a/scripts/run-turn.lua b/scripts/run-turn.lua index e5c90a7be..7ef29b00d 100644 --- a/scripts/run-turn.lua +++ b/scripts/run-turn.lua @@ -130,8 +130,9 @@ function process(rules, orders) end turn_begin() - - -- run the turn: + -- create orders for monsters: + plan_monsters() + -- read orders for players: if eressea.read_orders(orders) ~= 0 then print("could not read " .. orders) return -1 @@ -140,8 +141,9 @@ function process(rules, orders) if nmr_check(config.maxnmrs or 80)~=0 then return -1 end - plan_monsters() callbacks(rules, 'init') + + -- run the turn: turn_process() callbacks(rules, 'update') turn_end() -- ageing, etc. diff --git a/src/monsters.c b/src/monsters.c index 6f20a8c8b..b1ff0feba 100644 --- a/src/monsters.c +++ b/src/monsters.c @@ -214,10 +214,10 @@ int monster_attacks(unit * monster, bool rich_only) return result; } -static order *get_money_for_dragon(region * r, unit * udragon, int wanted) +static order *get_money_for_dragon(region * r, unit * u, int wanted) { int money; - bool attacks = attack_chance > 0.0; + bool attacks = (attack_chance > 0.0) && armedmen(u); /* falls genug geld in der region ist, treiben wir steuern ein. */ if (rmoney(r) >= wanted) { @@ -231,8 +231,8 @@ static order *get_money_for_dragon(region * r, unit * udragon, int wanted) /* falls der drache launisch ist, oder das regionssilber knapp, greift er alle an * und holt sich Silber von Einheiten, vorausgesetzt er bewacht bereits */ money = 0; - if (attacks && is_guard(udragon)) { - int m = monster_attacks(udragon, true); + if (attacks && is_guard(u)) { + int m = monster_attacks(u, true); if (m > 0) money += m; } @@ -623,11 +623,11 @@ static order *plan_dragon(unit * u) rc_wyrm = get_race(RC_WYRM); } - if (ta == NULL) { - move |= (rpeasants(r) == 0); /* when no peasants, move */ - move |= (rmoney(r) == 0); /* when no money, move */ + if (!move && ta == NULL) { + move = (rpeasants(r) == 0); /* when no peasants, move */ + move = move || (rmoney(r) == 0); /* when no money, move */ } - move |= chance(0.04); /* 4% chance to change your mind */ + move = move || chance(0.04); /* 4% chance to change your mind */ if (rc == rc_wyrm && !move) { unit *u2; @@ -739,6 +739,7 @@ void plan_monsters(faction * f) attrib *ta; order *long_order = NULL; bool can_move = true; + bool guarding; /* Ab hier nur noch Befehle fuer NPC-Einheiten. */ if (u->faction != f || u->number <= 0) { @@ -763,8 +764,9 @@ void plan_monsters(faction * f) /* Befehle muessen jede Runde neu gegeben werden: */ free_orders(&u->orders); - /* All monsters guard the region: */ - if (u->status < ST_FLEE && !monster_is_waiting(u) && r->land) { + guarding = is_guard(u); + /* All monsters want to guard the region: */ + if (!guarding && u->status < ST_FLEE && !monster_is_waiting(u) && r->land) { unit_addorder(u, create_order(K_GUARD, u->faction->locale, NULL)); } @@ -790,7 +792,7 @@ void plan_monsters(faction * f) else a_remove(&u->attribs, ta); } - else if (!r->land || is_guard(u)) { + else if (!r->land || guarding) { if (chance(attack_chance)) { int m = monster_attacks(u, false); if (m >= 0) { From 043949c8d002a9cb0b45c631dc37f617ace3237a Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 7 Jan 2020 20:28:43 +0100 Subject: [PATCH 05/14] =?UTF-8?q?Bug=202625=20PFLANZE=20Implementation=20a?= =?UTF-8?q?ufgekl=C3=A4rt,=20Tests=20geschrieben.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/tests/e2/trees.lua | 58 ++++++++++++++++++----- src/economy.c | 94 ++++++++++++-------------------------- src/monsters.c | 2 +- 3 files changed, 76 insertions(+), 78 deletions(-) diff --git a/scripts/tests/e2/trees.lua b/scripts/tests/e2/trees.lua index 9ead37099..7aa6fd6d8 100644 --- a/scripts/tests/e2/trees.lua +++ b/scripts/tests/e2/trees.lua @@ -21,7 +21,28 @@ function test_no_growth() assert_equal(0, r:get_resource('tree')) end -function test_plant_seeds() +-- hebalism < T6 cannot plant +function test_plant_fail() + set_turn(184) + assert_equal('summer', get_season()) + local f = faction.create('goblin') + local r = region.create(0, 0, 'plain') + r:set_resource('seed', 0) + r:set_resource('sapling', 0) + r:set_resource('tree', 0) + local u = unit.create(f, r) + u:set_skill('herbalism', 5) + u:add_item('seed', 40) + u:add_order("PFLANZE 20 Samen") + process_orders() + assert_equal(0, r:get_resource('seed')) + assert_equal(0, r:get_resource('sapling')) + assert_equal(0, r:get_resource('tree')) + assert_equal(40, u:get_item('seed')) +end + +-- T6+ herbalism allows planting seeds at 1:1 rates +function test_plant_summer() set_turn(184) assert_equal('summer', get_season()) local f = faction.create('goblin') @@ -34,33 +55,48 @@ function test_plant_seeds() u:add_item('seed', 40) u:add_order("PFLANZE 20 Samen") process_orders() - assert_equal(0, r:get_resource('sapling')) assert_equal(20, r:get_resource('seed')) + assert_equal(0, r:get_resource('sapling')) assert_equal(0, r:get_resource('tree')) assert_equal(20, u:get_item('seed')) end -function disable_test_plant_seeds_spring() - set_turn(202) +-- in spring, herbalism >= T12 plants saplings at 10:1 rate +function test_plant_spring_saplings() + set_turn(204) assert_equal('spring', get_season()) local f = faction.create('goblin') local r = region.create(0, 0, 'plain') r:set_resource('seed', 0) r:set_resource('sapling', 0) r:set_resource('tree', 0) - - process_orders() - assert_equal(0, r:get_resource('sapling')) - assert_equal(0, r:get_resource('seed')) - assert_equal(0, r:get_resource('tree')) - local u = unit.create(f, r) u:set_skill('herbalism', 20) u:add_item('seed', 40) u:add_order("PFLANZE 20 Samen") process_orders() - assert_equal(20, r:get_resource('sapling')) -- T12+ in Spring + assert_equal(2, r:get_resource('sapling')) assert_equal(0, r:get_resource('seed')) assert_equal(0, r:get_resource('tree')) assert_equal(20, u:get_item('seed')) end + +-- herbalism < T12 means we are still planting seeds at 1:1 +function test_plant_spring_seeds() + set_turn(204) + assert_equal('spring', get_season()) + local f = faction.create('goblin') + local r = region.create(0, 0, 'plain') + r:set_resource('seed', 0) + r:set_resource('sapling', 0) + r:set_resource('tree', 0) + local u = unit.create(f, r) + u:set_skill('herbalism', 10) + u:add_item('seed', 40) + u:add_order("PFLANZE 10 Samen") + process_orders() + assert_equal(0, r:get_resource('sapling')) + assert_equal(10, r:get_resource('seed')) + assert_equal(0, r:get_resource('tree')) + assert_equal(30, u:get_item('seed')) +end diff --git a/src/economy.c b/src/economy.c index 35e08ade6..15266647b 100644 --- a/src/economy.c +++ b/src/economy.c @@ -1682,84 +1682,39 @@ static void plant(unit * u, int raw) u, r, planted, itype->rtype)); } -static void planttrees(unit * u, int raw) +static void planttrees(region * r, int type, int n) { - int n, i, skill, planted = 0; - const resource_type *rtype; - region * r = u->region; - - if (!fval(r->terrain, LAND_REGION)) { - return; - } - - /* Mallornbaeume kann man nur in Mallornregionen zuechten */ - rtype = get_resourcetype(fval(r, RF_MALLORN) ? R_MALLORN_SEED : R_SEED); - - /* Skill pruefen */ - skill = effskill(u, SK_HERBALISM, NULL); - if (skill < 6) { - ADDMSG(&u->faction->msgs, - msg_feedback(u, u->thisorder, "plant_skills", - "skill minskill product", SK_HERBALISM, 6, rtype, 1)); - return; - } - if (fval(r, RF_MALLORN) && skill < 7) { - ADDMSG(&u->faction->msgs, - msg_feedback(u, u->thisorder, "plant_skills", - "skill minskill product", SK_HERBALISM, 7, rtype, 1)); - return; - } - - /* wenn eine Anzahl angegeben wurde, nur soviel verbrauchen */ - if (raw > skill * u->number) raw = skill * u->number; - n = get_pooled(u, rtype, GET_DEFAULT, raw); - if (n == 0) { - ADDMSG(&u->faction->msgs, - msg_feedback(u, u->thisorder, "resource_missing", "missing", rtype)); - return; - } - if (n > raw) n = raw; - - /* Fuer jeden Samen Talent*10% Erfolgschance. */ - for (i = n; i > 0; i--) { - if (rng_int() % 10 < skill) - planted++; - } - rsettrees(r, 0, rtrees(r, 0) + planted); - - /* Alles ok. Abziehen. */ - produceexp(u, SK_HERBALISM, u->number); - use_pooled(u, rtype, GET_DEFAULT, n); - - ADDMSG(&u->faction->msgs, msg_message("plant", - "unit region amount herb", u, r, planted, rtype)); + rsettrees(r, type, rtrees(r, type) + n); } /* zuechte baeume */ static void breedtrees(unit * u, int raw) { - int n, i, skill, planted = 0; + int n, i, skill, planted; const resource_type *rtype; season_t current_season = calendar_season(turn); region *r = u->region; - - /* Baeume zuechten geht nur im Fruehling */ - if (current_season != SEASON_SPRING) { - planttrees(u, raw); - return; - } + int minskill = 6; if (!fval(r->terrain, LAND_REGION)) { return; } /* Mallornbaeume kann man nur in Mallornregionen zuechten */ - rtype = get_resourcetype(fval(r, RF_MALLORN) ? R_MALLORN_SEED : R_SEED); + if (fval(r, RF_MALLORN)) { + ++minskill; + rtype = get_resourcetype(R_MALLORN_SEED); + } + else { + rtype = get_resourcetype(R_SEED); + } /* Skill pruefen */ skill = effskill(u, SK_HERBALISM, NULL); - if (skill < 12) { - planttrees(u, raw); + if (skill < minskill) { + ADDMSG(&u->faction->msgs, + msg_feedback(u, u->thisorder, "plant_skills", + "skill minskill product", SK_HERBALISM, 6, rtype, 1)); return; } @@ -1775,14 +1730,21 @@ static void breedtrees(unit * u, int raw) } if (n > raw) n = raw; - /* Fuer jeden Samen Talent*5% Erfolgschance. */ - for (i = n; i > 0; i--) { - if (rng_int() % 100 < skill * 5) - planted++; + if (skill >= 12 && current_season == SEASON_SPRING) { + // Plant saplings at a rate of 10:1 + int remain = n % 10; + planted = n / 10; + if ((remain > 0) && (rng_int() % 10) < remain) { + ++planted; + } + planttrees(r, 1, planted); + } + else { + planted = n; + planttrees(r, 0, planted); } - rsettrees(r, 1, rtrees(r, 1) + planted); - /* Alles ok. Abziehen. */ + /* Alles ok. Samen abziehen. */ produceexp(u, SK_HERBALISM, u->number); use_pooled(u, rtype, GET_DEFAULT, n); diff --git a/src/monsters.c b/src/monsters.c index b1ff0feba..76a0fabb6 100644 --- a/src/monsters.c +++ b/src/monsters.c @@ -217,7 +217,7 @@ int monster_attacks(unit * monster, bool rich_only) static order *get_money_for_dragon(region * r, unit * u, int wanted) { int money; - bool attacks = (attack_chance > 0.0) && armedmen(u); + bool attacks = (attack_chance > 0.0) && armedmen(u, false); /* falls genug geld in der region ist, treiben wir steuern ein. */ if (rmoney(r) >= wanted) { From bfd89687cd09dac80a76e2833efb13e7d7ed78a3 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 7 Jan 2020 20:37:21 +0100 Subject: [PATCH 06/14] when planting saplings, plant remaining seeds. --- scripts/tests/e2/trees.lua | 12 ++++++------ src/economy.c | 21 +++++++++------------ 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/scripts/tests/e2/trees.lua b/scripts/tests/e2/trees.lua index 7aa6fd6d8..85f3f64ca 100644 --- a/scripts/tests/e2/trees.lua +++ b/scripts/tests/e2/trees.lua @@ -71,14 +71,14 @@ function test_plant_spring_saplings() r:set_resource('sapling', 0) r:set_resource('tree', 0) local u = unit.create(f, r) - u:set_skill('herbalism', 20) - u:add_item('seed', 40) - u:add_order("PFLANZE 20 Samen") + u:set_skill('herbalism', 12) + u:add_item('seed', 20) + u:add_order("PFLANZE 20 Samen") -- limited by herbalism process_orders() - assert_equal(2, r:get_resource('sapling')) - assert_equal(0, r:get_resource('seed')) + assert_equal(1, r:get_resource('sapling')) + assert_equal(2, r:get_resource('seed')) assert_equal(0, r:get_resource('tree')) - assert_equal(20, u:get_item('seed')) + assert_equal(8, u:get_item('seed')) end -- herbalism < T12 means we are still planting seeds at 1:1 diff --git a/src/economy.c b/src/economy.c index 15266647b..7c91d08ee 100644 --- a/src/economy.c +++ b/src/economy.c @@ -1684,13 +1684,15 @@ static void plant(unit * u, int raw) static void planttrees(region * r, int type, int n) { - rsettrees(r, type, rtrees(r, type) + n); + if (n > 0) { + rsettrees(r, type, rtrees(r, type) + n); + } } /* zuechte baeume */ static void breedtrees(unit * u, int raw) { - int n, i, skill, planted; + int n, i, skill; const resource_type *rtype; season_t current_season = calendar_season(turn); region *r = u->region; @@ -1731,17 +1733,12 @@ static void breedtrees(unit * u, int raw) if (n > raw) n = raw; if (skill >= 12 && current_season == SEASON_SPRING) { - // Plant saplings at a rate of 10:1 - int remain = n % 10; - planted = n / 10; - if ((remain > 0) && (rng_int() % 10) < remain) { - ++planted; - } - planttrees(r, 1, planted); + // Plant saplings for every 10 seeds + planttrees(r, 0, n % 10); + planttrees(r, 1, n / 10); } else { - planted = n; - planttrees(r, 0, planted); + planttrees(r, 0, n); } /* Alles ok. Samen abziehen. */ @@ -1749,7 +1746,7 @@ static void breedtrees(unit * u, int raw) use_pooled(u, rtype, GET_DEFAULT, n); ADDMSG(&u->faction->msgs, msg_message("plant", - "unit region amount herb", u, r, planted, rtype)); + "unit region amount herb", u, r, n, rtype)); } /* zuechte pferde */ From 00ac549750627460f76c5658296d67e5e60d13dd Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Wed, 8 Jan 2020 20:57:29 +0100 Subject: [PATCH 07/14] idiot-proofing the newfactions file parser --- src/gmtool.c | 2 +- src/modules/autoseed.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/gmtool.c b/src/gmtool.c index d876b22bd..394f1efde 100644 --- a/src/gmtool.c +++ b/src/gmtool.c @@ -47,7 +47,7 @@ static int g_quit; int force_color = 0; -newfaction * new_players = 0; +newfaction * new_players = NULL; state *current_state = NULL; diff --git a/src/modules/autoseed.c b/src/modules/autoseed.c index fca838c2a..b74690745 100644 --- a/src/modules/autoseed.c +++ b/src/modules/autoseed.c @@ -87,17 +87,17 @@ newfaction *read_newfactions(const char *filename) if (fgets(buf, sizeof(buf), F) == NULL) break; + if (buf[0] == '#') { + continue; + } email[0] = '\0'; password[0] = '\0'; - if (sscanf(buf, "%54s %19s %7s %15s %4d", email, race, lang, + if (sscanf(buf, "%54s %19s %7s %15s %4d", email, race, lang, password, &alliance) < 3) { break; } - if (email[0] == '#') { - continue; - } if (email[0] == '\0') { break; } From 06f00cae53503342339f91f1b05a036c655456aa Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Mon, 13 Jan 2020 22:21:26 +0100 Subject: [PATCH 08/14] Bug 2632: no flying convoy --- src/spells/flyingship.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spells/flyingship.c b/src/spells/flyingship.c index fb6a8faeb..3bf9b61b1 100644 --- a/src/spells/flyingship.c +++ b/src/spells/flyingship.c @@ -56,7 +56,7 @@ int sp_flying_ship(castorder * co) if (pa->param[0]->flag == TARGET_NOTFOUND) return 0; sh = pa->param[0]->data.sh; - if (sh->type->construction->maxsize > 50) { + if (sh->number > 1 || sh->type->construction->maxsize > 50) { ADDMSG(&caster->faction->msgs, msg_feedback(caster, co->order, "error_flying_ship_too_big", "ship", sh)); return 0; From e02c1e9d0087838d1eb1940cdf1561d6420db537 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Mon, 13 Jan 2020 22:24:48 +0100 Subject: [PATCH 09/14] Bug 2631: Kein Konvoi mit Sonnensegel. --- src/give.c | 3 ++- src/items/speedsail.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/give.c b/src/give.c index ff1a3ac03..9dde388a0 100644 --- a/src/give.c +++ b/src/give.c @@ -12,6 +12,7 @@ /* attributes includes */ #include +#include /* kernel includes */ #include @@ -318,7 +319,7 @@ static void transfer_units(ship *s1, ship *s2) } static bool ship_cursed(const ship *sh) { - return a_find(sh->attribs, &at_curse) != NULL; + return a_find(sh->attribs, &at_curse) != NULL || a_find(sh->attribs, &at_speedup) != NULL; } message * give_ship(unit *u1, unit *u2, int n, order *ord) diff --git a/src/items/speedsail.c b/src/items/speedsail.c index 8a8f35d84..a1a623549 100644 --- a/src/items/speedsail.c +++ b/src/items/speedsail.c @@ -31,7 +31,7 @@ struct order *ord) ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "use_realworld_only", "")); } else { - if (u->ship) { + if (u->ship && u->ship->number == 1) { attrib *a = a_find(u->ship->attribs, &at_speedup); if (a == NULL) { a = a_add(&u->ship->attribs, a_new(&at_speedup)); From 6626cfe297825065f02be24f145c035d592afefc Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 14 Jan 2020 20:57:29 +0100 Subject: [PATCH 10/14] Bug 2633: implement combined trade limits. --- src/economy.c | 180 ++++++++++++++++++++++----------------------- src/economy.test.c | 33 ++++++++- 2 files changed, 120 insertions(+), 93 deletions(-) diff --git a/src/economy.c b/src/economy.c index 7c91d08ee..63aeb3fa1 100644 --- a/src/economy.c +++ b/src/economy.c @@ -1052,6 +1052,36 @@ const attrib_type at_luxuries = { "luxuries", NULL, free_luxuries, NULL, NULL, NULL }; +static const attrib_type at_trades = { + "trades", + DEFAULT_INIT, + DEFAULT_FINALIZE, + DEFAULT_AGE, + NO_WRITE, + NO_READ +}; + +static int trade_add(unit *u, int n) { + /* Ein Haendler kann nur 10 Gueter pro Talentpunkt handeln. */ + int k = u->number * 10 * effskill(u, SK_TRADE, NULL); + + attrib *a = a_find(u->attribs, &at_trades); + /* hat der Haendler bereits gehandelt, muss die Menge der bereits + * verkauften/gekauften Gueter abgezogen werden */ + if (!a) { + a = a_add(&u->attribs, a_new(&at_trades)); + } + else { + k -= a->data.i; + } + + if (n > k) n = k; + + /* die Menge der verkauften Gueter merken */ + a->data.i += n; + return n; +} + static void expandbuying(region * r, econ_request * buyorders) { const resource_type *rsilver = get_resourcetype(R_SILVER); @@ -1120,15 +1150,16 @@ static void expandbuying(region * r, econ_request * buyorders) a->data.v = items; i_change(&g_requests[j]->unit->items, ltype->itype, 1); use_pooled(u, rsilver, GET_DEFAULT, price); - if (u->n < 0) + if (u->n < 0) { u->n = 0; + } u->n += price; rsetmoney(r, rmoney(r) + price); /* Falls mehr als max_products Bauern ein Produkt verkauft haben, steigt - * der Preis Multiplikator fuer das Produkt um den Faktor 1. Der Zaehler - * wird wieder auf 0 gesetzt. */ + * der Preis Multiplikator fuer das Produkt um den Faktor 1. Der Zaehler + * wird wieder auf 0 gesetzt. */ if (++trade->number == max_products) { trade->number = 0; ++trade->multi; @@ -1158,15 +1189,6 @@ static void expandbuying(region * r, econ_request * buyorders) } } -attrib_type at_trades = { - "trades", - DEFAULT_INIT, - DEFAULT_FINALIZE, - DEFAULT_AGE, - NO_WRITE, - NO_READ -}; - bool trade_needs_castle(const terrain_type *terrain, const race *rc) { static int rc_change, terrain_change; @@ -1198,9 +1220,8 @@ static void buy(unit * u, econ_request ** buyorders, struct order *ord) { char token[128]; region *r = u->region; - int n, k; + int n; econ_request *o; - attrib *a; const item_type *itype = NULL; const luxury_type *ltype = NULL; keyword_t kwd; @@ -1239,30 +1260,12 @@ static void buy(unit * u, econ_request ** buyorders, struct order *ord) } } - /* Ein Haendler kann nur 10 Gueter pro Talentpunkt handeln. */ - k = u->number * 10 * effskill(u, SK_TRADE, NULL); - - /* hat der Haendler bereits gehandelt, muss die Menge der bereits - * verkauften/gekauften Gueter abgezogen werden */ - a = a_find(u->attribs, &at_trades); - if (!a) { - a = a_add(&u->attribs, a_new(&at_trades)); - } - else { - k -= a->data.i; - } - - if (n > k) n = k; - - if (!n) { + n = trade_add(u, n); + if (n <= 0) { cmistake(u, ord, 102, MSG_COMMERCE); return; } - assert(n >= 0); - /* die Menge der verkauften Gueter merken */ - a->data.i += n; - s = gettoken(token, sizeof(token)); itype = s ? finditemtype(s, u->faction->locale) : 0; if (itype != NULL) { @@ -1309,8 +1312,7 @@ static void expandselling(region * r, econ_request * sellorders, int limit) unit *maxowner = (unit *)NULL; building *maxb = (building *)NULL; building *b; - unit *u; - unit *hafenowner; + unit *hafenowner, *u; static int counter[MAXLUXURIES]; static int ncounter = 0; static int bt_cache; @@ -1386,6 +1388,8 @@ static void expandselling(region * r, econ_request * sellorders, int limit) int multi = r_demand(r, ltype); int i, price; int use = 0; + + u = g_requests[j]->unit; for (i = 0, search = luxurytypes; search != ltype; search = search->next) { /* TODO: this is slow and lame! */ ++i; @@ -1397,58 +1401,62 @@ static void expandselling(region * r, econ_request * sellorders, int limit) price = ltype->price * multi; if (money >= price) { - item *itm; - attrib *a; - u = g_requests[j]->unit; - a = a_find(u->attribs, &at_luxuries); - if (!a) { - a = a_add(&u->attribs, a_new(&at_luxuries)); + if (trade_add(u, 1) != 1) { + break; } - itm = (item *)a->data.v; - i_change(&itm, ltype->itype, 1); - a->data.v = itm; - ++use; - if (u->n < 0) { - u->n = 0; - } - - if (hafenowner) { - if (hafenowner->faction != u->faction) { - int abgezogenhafen = price / 10; - hafencollected += abgezogenhafen; - price -= abgezogenhafen; - money -= abgezogenhafen; + else { + item *itm; + attrib *a; + a = a_find(u->attribs, &at_luxuries); + if (!a) { + a = a_add(&u->attribs, a_new(&at_luxuries)); } - } - if (maxb) { - if (maxowner->faction != u->faction) { - int abgezogensteuer = price * tax_per_size[maxeffsize] / 100; - taxcollected += abgezogensteuer; - price -= abgezogensteuer; - money -= abgezogensteuer; + itm = (item *)a->data.v; + i_change(&itm, ltype->itype, 1); + a->data.v = itm; + ++use; + if (u->n < 0) { + u->n = 0; } - } - u->n += price; - change_money(u, price); - fset(u, UFL_LONGACTION | UFL_NOTMOVING); - /* r->money -= price; --- dies wird eben nicht ausgefuehrt, denn die - * Produkte koennen auch als Steuern eingetrieben werden. In der Region - * wurden Silberstuecke gegen Luxusgueter des selben Wertes eingetauscht! - * Falls mehr als max_products Kunden ein Produkt gekauft haben, sinkt - * die Nachfrage fuer das Produkt um 1. Der Zaehler wird wieder auf 0 - * gesetzt. */ - - if (++counter[i] > max_products) { - int d = r_demand(r, ltype); - if (d > 1) { - r_setdemand(r, ltype, d - 1); + if (hafenowner) { + if (hafenowner->faction != u->faction) { + int abgezogenhafen = price / 10; + hafencollected += abgezogenhafen; + price -= abgezogenhafen; + money -= abgezogenhafen; + } + } + if (maxb) { + if (maxowner->faction != u->faction) { + int abgezogensteuer = price * tax_per_size[maxeffsize] / 100; + taxcollected += abgezogensteuer; + price -= abgezogensteuer; + money -= abgezogensteuer; + } + } + u->n += price; + change_money(u, price); + fset(u, UFL_LONGACTION | UFL_NOTMOVING); + + /* r->money -= price; --- dies wird eben nicht ausgefuehrt, denn die + * Produkte koennen auch als Steuern eingetrieben werden. In der Region + * wurden Silberstuecke gegen Luxusgueter des selben Wertes eingetauscht! + * Falls mehr als max_products Kunden ein Produkt gekauft haben, sinkt + * die Nachfrage fuer das Produkt um 1. Der Zaehler wird wieder auf 0 + * gesetzt. */ + + if (++counter[i] > max_products) { + int d = r_demand(r, ltype); + if (d > 1) { + r_setdemand(r, ltype, d - 1); + } + counter[i] = 0; } - counter[i] = 0; } } if (use > 0) { - use_pooled(g_requests[j]->unit, ltype->itype->rtype, GET_DEFAULT, use); + use_pooled(u, ltype->itype->rtype, GET_DEFAULT, use); } } } @@ -1561,7 +1569,6 @@ static bool sell(unit * u, econ_request ** sellorders, struct order *ord) return false; } else { - attrib *a; econ_request *o; int k, available; @@ -1597,20 +1604,9 @@ static bool sell(unit * u, econ_request ** sellorders, struct order *ord) /* Ein Haendler kann nur 10 Gueter pro Talentpunkt handeln. */ k = u->number * 10 * effskill(u, SK_TRADE, NULL); - /* hat der Haendler bereits gehandelt, muss die Menge der bereits - * verkauften/gekauften Gueter abgezogen werden */ - a = a_find(u->attribs, &at_trades); - if (!a) { - a = a_add(&u->attribs, a_new(&at_trades)); - } - else { - k -= a->data.i; - } - if (n > k) n = k; assert(n >= 0); /* die Menge der verkauften Gueter merken */ - a->data.i += n; o = (econ_request *)calloc(1, sizeof(econ_request)); if (!o) abort(); o->unit = u; diff --git a/src/economy.test.c b/src/economy.test.c index e84bc17bf..034a6cf44 100644 --- a/src/economy.test.c +++ b/src/economy.test.c @@ -209,7 +209,7 @@ static void setup_terrains(CuTest *tc) { static region *setup_trade_region(CuTest *tc, const struct terrain_type *terrain) { region *r; item_type *it_luxury; - struct locale * lang = default_locale; + struct locale * lang = test_create_locale(); new_luxurytype(it_luxury = test_create_itemtype("balm"), 5); locale_setstring(lang, it_luxury->rtype->_name, it_luxury->rtype->_name); @@ -233,6 +233,36 @@ static unit *setup_trade_unit(CuTest *tc, region *r, const struct race *rc) { return u; } +static void test_trade_limits(CuTest *tc) { + region *r; + unit *u; + building *b; + const item_type *it_jewel, *it_balm; + + test_setup(); + setup_production(); + setup_terrains(tc); + init_terrains(); + r = setup_trade_region(tc, NULL); + b = test_create_building(r, test_create_buildingtype("castle")); + b->size = 2; + rsetpeasants(r, TRADE_FRACTION * 20); + it_jewel = it_find("jewel"); + u = test_create_unit(test_create_faction(NULL), r); + set_level(u, SK_TRADE, 1); + i_change(&u->items, it_find("money"), 500); + unit_addorder(u, create_order(K_BUY, u->faction->locale, "5 %s", + LOC(u->faction->locale, resourcename(it_jewel->rtype, 0)))); + it_balm = it_find("balm"); + i_change(&u->items, it_balm, 10); + unit_addorder(u, create_order(K_SELL, u->faction->locale, "10 %s", + LOC(u->faction->locale, resourcename(it_balm->rtype, 0)))); + produce(r); + CuAssertIntEquals(tc, 5, i_get(u->items, it_jewel)); + CuAssertIntEquals(tc, 5, i_get(u->items, it_balm)); + test_teardown(); +} + static void test_trade_needs_castle(CuTest *tc) { /* Handeln ist nur in Regionen mit Burgen möglich. */ race *rc; @@ -837,6 +867,7 @@ CuSuite *get_economy_suite(void) SUITE_ADD_TEST(suite, test_heroes_dont_recruit); SUITE_ADD_TEST(suite, test_tax_cmd); SUITE_ADD_TEST(suite, test_buy_cmd); + SUITE_ADD_TEST(suite, test_trade_limits); SUITE_ADD_TEST(suite, test_trade_needs_castle); SUITE_ADD_TEST(suite, test_trade_insect); SUITE_ADD_TEST(suite, test_maintain_buildings); From 27aac671a498eb6489e53ee9004faacee53c546a Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 14 Jan 2020 21:00:54 +0100 Subject: [PATCH 11/14] simplicate and add lightness --- src/economy.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/economy.c b/src/economy.c index 63aeb3fa1..bbc4c121a 100644 --- a/src/economy.c +++ b/src/economy.c @@ -1061,9 +1061,9 @@ static const attrib_type at_trades = { NO_READ }; -static int trade_add(unit *u, int n) { +static int trade_add(unit *u, int wants, int skill) { /* Ein Haendler kann nur 10 Gueter pro Talentpunkt handeln. */ - int k = u->number * 10 * effskill(u, SK_TRADE, NULL); + int k = u->number * 10 * skill; attrib *a = a_find(u->attribs, &at_trades); /* hat der Haendler bereits gehandelt, muss die Menge der bereits @@ -1075,11 +1075,11 @@ static int trade_add(unit *u, int n) { k -= a->data.i; } - if (n > k) n = k; + if (wants > k) wants = k; /* die Menge der verkauften Gueter merken */ - a->data.i += n; - return n; + a->data.i += wants; + return wants; } static void expandbuying(region * r, econ_request * buyorders) @@ -1220,7 +1220,7 @@ static void buy(unit * u, econ_request ** buyorders, struct order *ord) { char token[128]; region *r = u->region; - int n; + int n, skill = effskill(u, SK_TRADE, NULL); econ_request *o; const item_type *itype = NULL; const luxury_type *ltype = NULL; @@ -1260,7 +1260,7 @@ static void buy(unit * u, econ_request ** buyorders, struct order *ord) } } - n = trade_add(u, n); + n = trade_add(u, n, skill); if (n <= 0) { cmistake(u, ord, 102, MSG_COMMERCE); return; @@ -1386,10 +1386,11 @@ static void expandselling(region * r, econ_request * sellorders, int limit) const luxury_type *search = NULL; const luxury_type *ltype = g_requests[j]->data.trade.ltype; int multi = r_demand(r, ltype); - int i, price; + int i, price, skill; int use = 0; u = g_requests[j]->unit; + skill = effskill(u, SK_TRADE, NULL); for (i = 0, search = luxurytypes; search != ltype; search = search->next) { /* TODO: this is slow and lame! */ ++i; @@ -1401,7 +1402,7 @@ static void expandselling(region * r, econ_request * sellorders, int limit) price = ltype->price * multi; if (money >= price) { - if (trade_add(u, 1) != 1) { + if (trade_add(u, 1, skill) != 1) { break; } else { From 7aaa0fc301b7f79b54272ddcb125914dcef68c9a Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Wed, 15 Jan 2020 22:48:20 +0100 Subject: [PATCH 12/14] Bug 2633: Porting the test from my "fix" to prove that it wasn't required. --- src/economy.test.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/economy.test.c b/src/economy.test.c index e84bc17bf..034a6cf44 100644 --- a/src/economy.test.c +++ b/src/economy.test.c @@ -209,7 +209,7 @@ static void setup_terrains(CuTest *tc) { static region *setup_trade_region(CuTest *tc, const struct terrain_type *terrain) { region *r; item_type *it_luxury; - struct locale * lang = default_locale; + struct locale * lang = test_create_locale(); new_luxurytype(it_luxury = test_create_itemtype("balm"), 5); locale_setstring(lang, it_luxury->rtype->_name, it_luxury->rtype->_name); @@ -233,6 +233,36 @@ static unit *setup_trade_unit(CuTest *tc, region *r, const struct race *rc) { return u; } +static void test_trade_limits(CuTest *tc) { + region *r; + unit *u; + building *b; + const item_type *it_jewel, *it_balm; + + test_setup(); + setup_production(); + setup_terrains(tc); + init_terrains(); + r = setup_trade_region(tc, NULL); + b = test_create_building(r, test_create_buildingtype("castle")); + b->size = 2; + rsetpeasants(r, TRADE_FRACTION * 20); + it_jewel = it_find("jewel"); + u = test_create_unit(test_create_faction(NULL), r); + set_level(u, SK_TRADE, 1); + i_change(&u->items, it_find("money"), 500); + unit_addorder(u, create_order(K_BUY, u->faction->locale, "5 %s", + LOC(u->faction->locale, resourcename(it_jewel->rtype, 0)))); + it_balm = it_find("balm"); + i_change(&u->items, it_balm, 10); + unit_addorder(u, create_order(K_SELL, u->faction->locale, "10 %s", + LOC(u->faction->locale, resourcename(it_balm->rtype, 0)))); + produce(r); + CuAssertIntEquals(tc, 5, i_get(u->items, it_jewel)); + CuAssertIntEquals(tc, 5, i_get(u->items, it_balm)); + test_teardown(); +} + static void test_trade_needs_castle(CuTest *tc) { /* Handeln ist nur in Regionen mit Burgen möglich. */ race *rc; @@ -837,6 +867,7 @@ CuSuite *get_economy_suite(void) SUITE_ADD_TEST(suite, test_heroes_dont_recruit); SUITE_ADD_TEST(suite, test_tax_cmd); SUITE_ADD_TEST(suite, test_buy_cmd); + SUITE_ADD_TEST(suite, test_trade_limits); SUITE_ADD_TEST(suite, test_trade_needs_castle); SUITE_ADD_TEST(suite, test_trade_insect); SUITE_ADD_TEST(suite, test_maintain_buildings); From b88f9e8610dc1b3f01d98bea07c1a22b31d64d86 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Wed, 15 Jan 2020 23:08:11 +0100 Subject: [PATCH 13/14] remove duplicate calculation. --- src/economy.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/economy.c b/src/economy.c index 35e08ade6..0f7660308 100644 --- a/src/economy.c +++ b/src/economy.c @@ -1495,7 +1495,7 @@ static bool sell(unit * u, econ_request ** sellorders, struct order *ord) bool unlimited = true; const item_type *itype; const luxury_type *ltype; - int n, i; + int n, k; region *r = u->region; const char *s; keyword_t kwd; @@ -1546,8 +1546,9 @@ static bool sell(unit * u, econ_request ** sellorders, struct order *ord) } /* Ein Haendler kann nur 10 Gueter pro Talentpunkt verkaufen. */ - i = u->number * 10 * effskill(u, SK_TRADE, NULL); - if (n > i) n = i; + /* Ein Haendler kann nur 10 Gueter pro Talentpunkt handeln. */ + k = u->number * 10 * effskill(u, SK_TRADE, NULL); + if (n > k) n = k; if (!n) { cmistake(u, ord, 54, MSG_COMMERCE); @@ -1563,7 +1564,7 @@ static bool sell(unit * u, econ_request ** sellorders, struct order *ord) else { attrib *a; econ_request *o; - int k, available; + int available; if (!r->land || !r_demand(r, ltype)) { cmistake(u, ord, 263, MSG_COMMERCE); @@ -1594,9 +1595,6 @@ static bool sell(unit * u, econ_request ** sellorders, struct order *ord) * produktion, wo fuer jedes produkt einzeln eine obere limite * existiert, so dass man arrays von orders machen kann. */ - /* Ein Haendler kann nur 10 Gueter pro Talentpunkt handeln. */ - k = u->number * 10 * effskill(u, SK_TRADE, NULL); - /* hat der Haendler bereits gehandelt, muss die Menge der bereits * verkauften/gekauften Gueter abgezogen werden */ a = a_find(u->attribs, &at_trades); From 3b09b401f75fe863016d0c239d46b02d08a4c45f Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Thu, 16 Jan 2020 18:19:25 +0100 Subject: [PATCH 14/14] Revert unnecessary trade limit bugfix This reverts commit 27aac671a498eb6489e53ee9004faacee53c546a. --- src/economy.c | 189 +++++++++++++++++++++--------------------- src/give.c | 3 +- src/items/speedsail.c | 2 +- 3 files changed, 97 insertions(+), 97 deletions(-) diff --git a/src/economy.c b/src/economy.c index bbc4c121a..482b8d959 100644 --- a/src/economy.c +++ b/src/economy.c @@ -1052,36 +1052,6 @@ const attrib_type at_luxuries = { "luxuries", NULL, free_luxuries, NULL, NULL, NULL }; -static const attrib_type at_trades = { - "trades", - DEFAULT_INIT, - DEFAULT_FINALIZE, - DEFAULT_AGE, - NO_WRITE, - NO_READ -}; - -static int trade_add(unit *u, int wants, int skill) { - /* Ein Haendler kann nur 10 Gueter pro Talentpunkt handeln. */ - int k = u->number * 10 * skill; - - attrib *a = a_find(u->attribs, &at_trades); - /* hat der Haendler bereits gehandelt, muss die Menge der bereits - * verkauften/gekauften Gueter abgezogen werden */ - if (!a) { - a = a_add(&u->attribs, a_new(&at_trades)); - } - else { - k -= a->data.i; - } - - if (wants > k) wants = k; - - /* die Menge der verkauften Gueter merken */ - a->data.i += wants; - return wants; -} - static void expandbuying(region * r, econ_request * buyorders) { const resource_type *rsilver = get_resourcetype(R_SILVER); @@ -1150,16 +1120,15 @@ static void expandbuying(region * r, econ_request * buyorders) a->data.v = items; i_change(&g_requests[j]->unit->items, ltype->itype, 1); use_pooled(u, rsilver, GET_DEFAULT, price); - if (u->n < 0) { + if (u->n < 0) u->n = 0; - } u->n += price; rsetmoney(r, rmoney(r) + price); /* Falls mehr als max_products Bauern ein Produkt verkauft haben, steigt - * der Preis Multiplikator fuer das Produkt um den Faktor 1. Der Zaehler - * wird wieder auf 0 gesetzt. */ + * der Preis Multiplikator fuer das Produkt um den Faktor 1. Der Zaehler + * wird wieder auf 0 gesetzt. */ if (++trade->number == max_products) { trade->number = 0; ++trade->multi; @@ -1189,6 +1158,15 @@ static void expandbuying(region * r, econ_request * buyorders) } } +attrib_type at_trades = { + "trades", + DEFAULT_INIT, + DEFAULT_FINALIZE, + DEFAULT_AGE, + NO_WRITE, + NO_READ +}; + bool trade_needs_castle(const terrain_type *terrain, const race *rc) { static int rc_change, terrain_change; @@ -1220,8 +1198,9 @@ static void buy(unit * u, econ_request ** buyorders, struct order *ord) { char token[128]; region *r = u->region; - int n, skill = effskill(u, SK_TRADE, NULL); + int n, k; econ_request *o; + attrib *a; const item_type *itype = NULL; const luxury_type *ltype = NULL; keyword_t kwd; @@ -1260,12 +1239,30 @@ static void buy(unit * u, econ_request ** buyorders, struct order *ord) } } - n = trade_add(u, n, skill); - if (n <= 0) { + /* Ein Haendler kann nur 10 Gueter pro Talentpunkt handeln. */ + k = u->number * 10 * effskill(u, SK_TRADE, NULL); + + /* hat der Haendler bereits gehandelt, muss die Menge der bereits + * verkauften/gekauften Gueter abgezogen werden */ + a = a_find(u->attribs, &at_trades); + if (!a) { + a = a_add(&u->attribs, a_new(&at_trades)); + } + else { + k -= a->data.i; + } + + if (n > k) n = k; + + if (!n) { cmistake(u, ord, 102, MSG_COMMERCE); return; } + assert(n >= 0); + /* die Menge der verkauften Gueter merken */ + a->data.i += n; + s = gettoken(token, sizeof(token)); itype = s ? finditemtype(s, u->faction->locale) : 0; if (itype != NULL) { @@ -1312,7 +1309,8 @@ static void expandselling(region * r, econ_request * sellorders, int limit) unit *maxowner = (unit *)NULL; building *maxb = (building *)NULL; building *b; - unit *hafenowner, *u; + unit *u; + unit *hafenowner; static int counter[MAXLUXURIES]; static int ncounter = 0; static int bt_cache; @@ -1386,11 +1384,8 @@ static void expandselling(region * r, econ_request * sellorders, int limit) const luxury_type *search = NULL; const luxury_type *ltype = g_requests[j]->data.trade.ltype; int multi = r_demand(r, ltype); - int i, price, skill; + int i, price; int use = 0; - - u = g_requests[j]->unit; - skill = effskill(u, SK_TRADE, NULL); for (i = 0, search = luxurytypes; search != ltype; search = search->next) { /* TODO: this is slow and lame! */ ++i; @@ -1402,62 +1397,58 @@ static void expandselling(region * r, econ_request * sellorders, int limit) price = ltype->price * multi; if (money >= price) { - if (trade_add(u, 1, skill) != 1) { - break; + item *itm; + attrib *a; + u = g_requests[j]->unit; + a = a_find(u->attribs, &at_luxuries); + if (!a) { + a = a_add(&u->attribs, a_new(&at_luxuries)); + } + itm = (item *)a->data.v; + i_change(&itm, ltype->itype, 1); + a->data.v = itm; + ++use; + if (u->n < 0) { + u->n = 0; } - else { - item *itm; - attrib *a; - a = a_find(u->attribs, &at_luxuries); - if (!a) { - a = a_add(&u->attribs, a_new(&at_luxuries)); - } - itm = (item *)a->data.v; - i_change(&itm, ltype->itype, 1); - a->data.v = itm; - ++use; - if (u->n < 0) { - u->n = 0; - } - if (hafenowner) { - if (hafenowner->faction != u->faction) { - int abgezogenhafen = price / 10; - hafencollected += abgezogenhafen; - price -= abgezogenhafen; - money -= abgezogenhafen; - } + if (hafenowner) { + if (hafenowner->faction != u->faction) { + int abgezogenhafen = price / 10; + hafencollected += abgezogenhafen; + price -= abgezogenhafen; + money -= abgezogenhafen; } - if (maxb) { - if (maxowner->faction != u->faction) { - int abgezogensteuer = price * tax_per_size[maxeffsize] / 100; - taxcollected += abgezogensteuer; - price -= abgezogensteuer; - money -= abgezogensteuer; - } + } + if (maxb) { + if (maxowner->faction != u->faction) { + int abgezogensteuer = price * tax_per_size[maxeffsize] / 100; + taxcollected += abgezogensteuer; + price -= abgezogensteuer; + money -= abgezogensteuer; } - u->n += price; - change_money(u, price); - fset(u, UFL_LONGACTION | UFL_NOTMOVING); + } + u->n += price; + change_money(u, price); + fset(u, UFL_LONGACTION | UFL_NOTMOVING); - /* r->money -= price; --- dies wird eben nicht ausgefuehrt, denn die - * Produkte koennen auch als Steuern eingetrieben werden. In der Region - * wurden Silberstuecke gegen Luxusgueter des selben Wertes eingetauscht! - * Falls mehr als max_products Kunden ein Produkt gekauft haben, sinkt - * die Nachfrage fuer das Produkt um 1. Der Zaehler wird wieder auf 0 - * gesetzt. */ + /* r->money -= price; --- dies wird eben nicht ausgefuehrt, denn die + * Produkte koennen auch als Steuern eingetrieben werden. In der Region + * wurden Silberstuecke gegen Luxusgueter des selben Wertes eingetauscht! + * Falls mehr als max_products Kunden ein Produkt gekauft haben, sinkt + * die Nachfrage fuer das Produkt um 1. Der Zaehler wird wieder auf 0 + * gesetzt. */ - if (++counter[i] > max_products) { - int d = r_demand(r, ltype); - if (d > 1) { - r_setdemand(r, ltype, d - 1); - } - counter[i] = 0; + if (++counter[i] > max_products) { + int d = r_demand(r, ltype); + if (d > 1) { + r_setdemand(r, ltype, d - 1); } + counter[i] = 0; } } if (use > 0) { - use_pooled(u, ltype->itype->rtype, GET_DEFAULT, use); + use_pooled(g_requests[j]->unit, ltype->itype->rtype, GET_DEFAULT, use); } } } @@ -1504,7 +1495,7 @@ static bool sell(unit * u, econ_request ** sellorders, struct order *ord) bool unlimited = true; const item_type *itype; const luxury_type *ltype; - int n, i; + int n, k; region *r = u->region; const char *s; keyword_t kwd; @@ -1555,8 +1546,9 @@ static bool sell(unit * u, econ_request ** sellorders, struct order *ord) } /* Ein Haendler kann nur 10 Gueter pro Talentpunkt verkaufen. */ - i = u->number * 10 * effskill(u, SK_TRADE, NULL); - if (n > i) n = i; + /* Ein Haendler kann nur 10 Gueter pro Talentpunkt handeln. */ + k = u->number * 10 * effskill(u, SK_TRADE, NULL); + if (n > k) n = k; if (!n) { cmistake(u, ord, 54, MSG_COMMERCE); @@ -1570,8 +1562,9 @@ static bool sell(unit * u, econ_request ** sellorders, struct order *ord) return false; } else { + attrib *a; econ_request *o; - int k, available; + int available; if (!r->land || !r_demand(r, ltype)) { cmistake(u, ord, 263, MSG_COMMERCE); @@ -1602,12 +1595,20 @@ static bool sell(unit * u, econ_request ** sellorders, struct order *ord) * produktion, wo fuer jedes produkt einzeln eine obere limite * existiert, so dass man arrays von orders machen kann. */ - /* Ein Haendler kann nur 10 Gueter pro Talentpunkt handeln. */ - k = u->number * 10 * effskill(u, SK_TRADE, NULL); + /* hat der Haendler bereits gehandelt, muss die Menge der bereits + * verkauften/gekauften Gueter abgezogen werden */ + a = a_find(u->attribs, &at_trades); + if (!a) { + a = a_add(&u->attribs, a_new(&at_trades)); + } + else { + k -= a->data.i; + } if (n > k) n = k; assert(n >= 0); /* die Menge der verkauften Gueter merken */ + a->data.i += n; o = (econ_request *)calloc(1, sizeof(econ_request)); if (!o) abort(); o->unit = u; diff --git a/src/give.c b/src/give.c index 9dde388a0..ff1a3ac03 100644 --- a/src/give.c +++ b/src/give.c @@ -12,7 +12,6 @@ /* attributes includes */ #include -#include /* kernel includes */ #include @@ -319,7 +318,7 @@ static void transfer_units(ship *s1, ship *s2) } static bool ship_cursed(const ship *sh) { - return a_find(sh->attribs, &at_curse) != NULL || a_find(sh->attribs, &at_speedup) != NULL; + return a_find(sh->attribs, &at_curse) != NULL; } message * give_ship(unit *u1, unit *u2, int n, order *ord) diff --git a/src/items/speedsail.c b/src/items/speedsail.c index a1a623549..8a8f35d84 100644 --- a/src/items/speedsail.c +++ b/src/items/speedsail.c @@ -31,7 +31,7 @@ struct order *ord) ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "use_realworld_only", "")); } else { - if (u->ship && u->ship->number == 1) { + if (u->ship) { attrib *a = a_find(u->ship->attribs, &at_speedup); if (a == NULL) { a = a_add(&u->ship->attribs, a_new(&at_speedup));