Merge lp:~widelands-dev/widelands/bug-1829471-worker-preciousness into lp:widelands

Proposed by GunChleoc
Status: Merged
Merged at revision: 9115
Proposed branch: lp:~widelands-dev/widelands/bug-1829471-worker-preciousness
Merge into: lp:widelands
Diff against target: 879 lines (+264/-158)
27 files modified
data/tribes/atlanteans.lua (+0/-1)
data/tribes/barbarians.lua (+0/-1)
data/tribes/buildings/productionsites/atlanteans/horsefarm/init.lua (+0/-1)
data/tribes/buildings/productionsites/barbarians/cattlefarm/init.lua (+0/-1)
data/tribes/buildings/productionsites/empire/donkeyfarm/init.lua (+0/-1)
data/tribes/empire.lua (+0/-1)
data/tribes/frisians.lua (+0/-1)
data/tribes/workers/atlanteans/horse/init.lua (+6/-0)
data/tribes/workers/atlanteans/soldier/init.lua (+6/-0)
data/tribes/workers/barbarians/ox/init.lua (+6/-0)
data/tribes/workers/barbarians/soldier/init.lua (+6/-0)
data/tribes/workers/empire/donkey/init.lua (+6/-0)
data/tribes/workers/empire/soldier/init.lua (+6/-0)
data/tribes/workers/frisians/reindeer/init.lua (+6/-0)
data/tribes/workers/frisians/soldier/init.lua (+6/-0)
doc/sphinx/source/lua_tribes_workers.rst.org (+62/-54)
src/ai/ai_help_structs.h (+1/-0)
src/ai/ai_hints.cc (+25/-1)
src/ai/ai_hints.h (+27/-5)
src/ai/defaultai.cc (+76/-64)
src/logic/map_objects/tribes/tribe_descr.cc (+0/-5)
src/logic/map_objects/tribes/tribe_descr.h (+0/-2)
src/logic/map_objects/tribes/tribes.cc (+1/-1)
src/logic/map_objects/tribes/ware_descr.cc (+5/-14)
src/logic/map_objects/tribes/ware_descr.h (+9/-5)
src/logic/map_objects/tribes/worker_descr.cc (+1/-0)
src/logic/map_objects/tribes/worker_descr.h (+9/-0)
To merge this branch: bzr merge lp:~widelands-dev/widelands/bug-1829471-worker-preciousness
Reviewer Review Type Date Requested Status
hessenfarmer Approve
TiborB Approve
Review via email: mp+367608@code.launchpad.net

Commit message

Some AI code cleanups

- Implement AI hints for workers
- Deduce whether a building is a barracks or recruits other workers from building outputs
- Move ware preciousness into a new AI hint object
- Fixed warning about signed/unsigned comparisons.

Description of the change

There is an assert that is failing now. Since I am not very familiar with the AI code, I don't know why it is there. @Tibor: Do you remember why you put it there?

To post a comment you must log in.
Revision history for this message
TiborB (tiborb95) wrote :

See my comments below

BTW I am doing some training, I hope this will not interfere. Once it is merged, I will re-train too..

Revision history for this message
TiborB (tiborb95) wrote :

The logic is / was you cannot have max preciousness if you do not produce anything...

Revision history for this message
GunChleoc (gunchleoc) wrote :

Thanks, that fixed it!

There should be no real impact on training from this branch. This is a pure refactoring without any intended semantic changes except that the reindeer farm now has a minimum preciousness of 2.

Revision history for this message
GunChleoc (gunchleoc) wrote :

BTW that assert is already broken in trunk.

Revision history for this message
TiborB (tiborb95) wrote :

I will investigate that assert....

Revision history for this message
TiborB (tiborb95) wrote :

I am getting the failing assert for atlanteans horsefarm and barbarian cattlefarm - in trunk... They have max_needed_preciousness 1.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 5015. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/534229981.
Appveyor build 4796. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_bug_1829471_worker_preciousness-4796.

Revision history for this message
TiborB (tiborb95) wrote :

Was you considering making barracks a recruitment? Because preciousness of barracks (output) is now hardcoded to 5, if I am right.

Revision history for this message
GunChleoc (gunchleoc) wrote :

I was only focusing on the code that hessenfarmer changed for the carrier2, but I think not having the barracks hard-coded would also be a good idea. I'll have a look.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Done. No need to investigate the assert failure in trunk further, because it is fixed in this branch. It slipped past us when we reviewed hessenfarmer's branch.

Revision history for this message
hessenfarmer (stephan-lutz) wrote :

The failed assert came definitly from my changes. I did not compile and test them in debug, cause my machine is so slow. Sorry about that.
The problem is we were handling the recruitment as a producing building so it needed preciousness while in another part we handled it like a non producing building asserting that preciousness is 0.

The desired behaviour should be to handle the barracks and the recruiter like any other building with some output. This needs 2 things the definition of a preciousness for the soldier and the second carrier and some good decisiongates to be taken into account for determining neededness and priority for both of them.

Revision history for this message
TiborB (tiborb95) wrote :

AI code is simply overcomplicated...
Animals are also workers?
Do we expect that same workers will have no preciousness set?

The overall change looks good to me

review: Approve
Revision history for this message
hessenfarmer (stephan-lutz) wrote :

As soon as a worker is produced by a building he needs a preciousness (if we think of schools for example). If it is created in the warehouses he does not need a preciousness.

Maybe we have a problem now with the frisian reindeer farm, as it will now be identified as a recruitment site. this will limit the number of them to 2 which is far too less in terms of fur production. In my opinion we need to ensure, that the normal production part of a building takes precedence. But I need to make some tests to confirm this issue.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Confirmed by looking at the code and fixed. It's fine that you're on release builds, I used to do that too before I bought my new machine.

And yes, animals are carriers, so they are workers.

Unlike for wares, preciousness is not mandatory for workers, since most of them aren't recruited by buildings.

Revision history for this message
hessenfarmer (stephan-lutz) wrote :

Hmm. I thought we are aiming at having more of the definition stuf in lua so we can change things without the need to recompile. I have the feeling that part of this branch is going in opposite direction. Fo the design of a new tribe I think I would like to have some flags in lua better than determine things from other values, cause this is giving us more flexibility.

Revision history for this message
hessenfarmer (stephan-lutz) wrote :

One thing I would like to have is a definition of normal AI limit to be able to have the limit values currently hardcoded in lua.

Revision history for this message
GunChleoc (gunchleoc) wrote :

There is a difference between hard-coding values like "preciousness = 5" and automatically deducing building types that get a completely different treatment and algorithm by the AI anyway.

Revision history for this message
GunChleoc (gunchleoc) wrote :

P.S. What we probably really should do here is to treat output workers in the exact same way as output wares and get rid of the special treatment code for recruiting sites, but that is out of scope for this branch.

Revision history for this message
GunChleoc (gunchleoc) wrote :

> One thing I would like to have is a definition of normal AI limit to be able to have the limit values currently hardcoded in lua.

Agreed, I have added it to the bug. I don't want to do it in this branch though, because then we will have to re-review the code in this branch for every new idea that we can come up. I prefer having separate branches for each issue.

Revision history for this message
hessenfarmer (stephan-lutz) wrote :

> There is a difference between hard-coding values like "preciousness = 5" and
> automatically deducing building types that get a completely different
> treatment and algorithm by the AI anyway.

Agreed on the difference. But not deducing the types and having flags instead gives us more flexibility. However for the moment being we will try to get this in until we need it the other way round again.

Revision history for this message
hessenfarmer (stephan-lutz) wrote :

I think the deduction of Recruitment of second carrier will not work this way as Outputs of recruitment sites contains the worker so it isn't empty. see inline comment as well

Revision history for this message
hessenfarmer (stephan-lutz) wrote :

Forget my last comment. I had the wrong conception of bo.outputs

So for me Code looks good will test this tonight.

review: Approve
Revision history for this message
hessenfarmer (stephan-lutz) wrote :

I have tested it now and found no anomalies (just tested with release build)

so from my side this could go in, although I would keep the first bug open.

Revision history for this message
hessenfarmer (stephan-lutz) wrote :

@bunnybot merge

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'data/tribes/atlanteans.lua'
2--- data/tribes/atlanteans.lua 2018-07-29 13:49:09 +0000
3+++ data/tribes/atlanteans.lua 2019-05-19 16:18:33 +0000
4@@ -375,7 +375,6 @@
5 soldier = "atlanteans_soldier",
6 ship = "atlanteans_ship",
7 port = "atlanteans_port",
8- barracks = "atlanteans_barracks",
9 ironore = "iron_ore",
10 rawlog = "log",
11 refinedlog = "planks",
12
13=== modified file 'data/tribes/barbarians.lua'
14--- data/tribes/barbarians.lua 2018-07-29 13:49:09 +0000
15+++ data/tribes/barbarians.lua 2019-05-19 16:18:33 +0000
16@@ -308,7 +308,6 @@
17 soldier = "barbarians_soldier",
18 ship = "barbarians_ship",
19 port = "barbarians_port",
20- barracks = "barbarians_barracks",
21 ironore = "iron_ore",
22 rawlog = "log",
23 refinedlog = "blackwood",
24
25=== modified file 'data/tribes/buildings/productionsites/atlanteans/horsefarm/init.lua'
26--- data/tribes/buildings/productionsites/atlanteans/horsefarm/init.lua 2019-05-15 06:29:24 +0000
27+++ data/tribes/buildings/productionsites/atlanteans/horsefarm/init.lua 2019-05-19 16:18:33 +0000
28@@ -31,7 +31,6 @@
29 },
30
31 aihints = {
32- recruitment = true,
33 prohibited_till = 610,
34 },
35
36
37=== modified file 'data/tribes/buildings/productionsites/barbarians/cattlefarm/init.lua'
38--- data/tribes/buildings/productionsites/barbarians/cattlefarm/init.lua 2019-05-15 06:29:24 +0000
39+++ data/tribes/buildings/productionsites/barbarians/cattlefarm/init.lua 2019-05-19 16:18:33 +0000
40@@ -31,7 +31,6 @@
41 },
42
43 aihints = {
44- recruitment = true,
45 prohibited_till = 610,
46 },
47
48
49=== modified file 'data/tribes/buildings/productionsites/empire/donkeyfarm/init.lua'
50--- data/tribes/buildings/productionsites/empire/donkeyfarm/init.lua 2019-05-15 06:29:24 +0000
51+++ data/tribes/buildings/productionsites/empire/donkeyfarm/init.lua 2019-05-19 16:18:33 +0000
52@@ -31,7 +31,6 @@
53 },
54
55 aihints = {
56- recruitment = true,
57 prohibited_till = 610,
58 },
59
60
61=== modified file 'data/tribes/empire.lua'
62--- data/tribes/empire.lua 2018-07-29 13:49:09 +0000
63+++ data/tribes/empire.lua 2019-05-19 16:18:33 +0000
64@@ -350,7 +350,6 @@
65 soldier = "empire_soldier",
66 ship = "empire_ship",
67 port = "empire_port",
68- barracks = "empire_barracks",
69 ironore = "iron_ore",
70 rawlog = "log",
71 refinedlog = "planks",
72
73=== modified file 'data/tribes/frisians.lua'
74--- data/tribes/frisians.lua 2019-02-28 11:03:51 +0000
75+++ data/tribes/frisians.lua 2019-05-19 16:18:33 +0000
76@@ -349,7 +349,6 @@
77 soldier = "frisians_soldier",
78 ship = "frisians_ship",
79 port = "frisians_port",
80- barracks = "frisians_barracks",
81 ironore = "iron_ore",
82 rawlog = "log",
83 refinedlog = "brick",
84
85=== modified file 'data/tribes/workers/atlanteans/horse/init.lua'
86--- data/tribes/workers/atlanteans/horse/init.lua 2017-02-12 09:10:57 +0000
87+++ data/tribes/workers/atlanteans/horse/init.lua 2019-05-19 16:18:33 +0000
88@@ -24,4 +24,10 @@
89 ware_hotspot = {-2, 12},
90
91 animations = animations,
92+
93+ aihints = {
94+ preciousness = {
95+ atlanteans = 2
96+ },
97+ }
98 }
99
100=== modified file 'data/tribes/workers/atlanteans/soldier/init.lua'
101--- data/tribes/workers/atlanteans/soldier/init.lua 2019-05-18 12:05:22 +0000
102+++ data/tribes/workers/atlanteans/soldier/init.lua 2019-05-19 16:18:33 +0000
103@@ -284,6 +284,12 @@
104 pictures = path.list_files(dirname .. "evade_level?.png"),
105 },
106
107+ aihints = {
108+ preciousness = {
109+ atlanteans = 5
110+ },
111+ },
112+
113 -- Random animations for battle
114 -- TODO(GunChleoc): Make more animations to use the random function
115 attack_success_w = {
116
117=== modified file 'data/tribes/workers/barbarians/ox/init.lua'
118--- data/tribes/workers/barbarians/ox/init.lua 2017-02-12 09:10:57 +0000
119+++ data/tribes/workers/barbarians/ox/init.lua 2019-05-19 16:18:33 +0000
120@@ -25,4 +25,10 @@
121 ware_hotspot = { -2, 13 },
122
123 animations = animations,
124+
125+ aihints = {
126+ preciousness = {
127+ barbarians = 2
128+ },
129+ }
130 }
131
132=== modified file 'data/tribes/workers/barbarians/soldier/init.lua'
133--- data/tribes/workers/barbarians/soldier/init.lua 2019-04-26 19:10:45 +0000
134+++ data/tribes/workers/barbarians/soldier/init.lua 2019-05-19 16:18:33 +0000
135@@ -110,6 +110,12 @@
136 pictures = path.list_files(dirname .. "evade_level?.png"),
137 },
138
139+ aihints = {
140+ preciousness = {
141+ barbarians = 5
142+ },
143+ },
144+
145 -- Random animations for battle
146 attack_success_w = {
147 atk_ok_w = all_levels_bar,
148
149=== modified file 'data/tribes/workers/empire/donkey/init.lua'
150--- data/tribes/workers/empire/donkey/init.lua 2017-02-12 09:10:57 +0000
151+++ data/tribes/workers/empire/donkey/init.lua 2019-05-19 16:18:33 +0000
152@@ -24,4 +24,10 @@
153 ware_hotspot = { -2, 8 },
154
155 animations = animations,
156+
157+ aihints = {
158+ preciousness = {
159+ empire = 2
160+ },
161+ }
162 }
163
164=== modified file 'data/tribes/workers/empire/soldier/init.lua'
165--- data/tribes/workers/empire/soldier/init.lua 2019-04-26 19:10:45 +0000
166+++ data/tribes/workers/empire/soldier/init.lua 2019-05-19 16:18:33 +0000
167@@ -110,6 +110,12 @@
168 pictures = path.list_files(dirname .. "evade_level?.png"),
169 },
170
171+ aihints = {
172+ preciousness = {
173+ empire = 5
174+ },
175+ },
176+
177 -- Random animations for battle
178 attack_success_w = {
179 atk_ok_w = all_levels_emp,
180
181=== modified file 'data/tribes/workers/frisians/reindeer/init.lua'
182--- data/tribes/workers/frisians/reindeer/init.lua 2018-02-06 11:17:48 +0000
183+++ data/tribes/workers/frisians/reindeer/init.lua 2019-05-19 16:18:33 +0000
184@@ -24,4 +24,10 @@
185 ware_hotspot = { 0, 18 },
186
187 animations = animations,
188+
189+ aihints = {
190+ preciousness = {
191+ frisians = 2
192+ },
193+ }
194 }
195
196=== modified file 'data/tribes/workers/frisians/soldier/init.lua'
197--- data/tribes/workers/frisians/soldier/init.lua 2019-05-18 20:43:25 +0000
198+++ data/tribes/workers/frisians/soldier/init.lua 2019-05-19 16:18:33 +0000
199@@ -434,6 +434,12 @@
200 pictures = path.list_files (dirname .. "evade_level?.png"),
201 },
202
203+ aihints = {
204+ preciousness = {
205+ frisians = 5
206+ },
207+ },
208+
209 -- Random animations for battle
210 attack_success_e = {
211 atk_ok_1_w = fri_rookie,
212
213=== modified file 'doc/sphinx/source/lua_tribes_workers.rst.org'
214--- doc/sphinx/source/lua_tribes_workers.rst.org 2018-11-30 10:36:42 +0000
215+++ doc/sphinx/source/lua_tribes_workers.rst.org 2019-05-19 16:18:33 +0000
216@@ -17,60 +17,68 @@
217
218 Workers are defined with Lua functions called ``new_<worker_type>_type{table}``. The contents of ``table`` depend on the type of worker that you are defining. The common properties shared by all workers are:
219
220- **msgctxt**: The context that Gettext will use to disambiguate the
221- translations for strings in this table.
222-
223- **name**: A string containing the internal name of this worker.
224-
225- **descname**: The translatable display name. Use ``pgettext`` with the
226- ``msgctxt`` above to fetch the string.
227-
228- **helptext_script**: The full path to the ``helptexts.lua`` script for this worker.
229-
230- **icon**: The full path to the menu icon for this worker.
231-
232- **vision_range**
233- The size of the radius that the worker sees.
234-
235- **buildcost**
236- *Optional*. A table with the wares and workers used by warehouses to
237- create this worker, containing warename - amount pairs, e.g.::
238-
239- buildcost = { atlanteans_carrier = 1, hammer = 1 }
240-
241- **default_target_quantity**:
242- *Optional*. An int defining the default target quantity for the worker's
243- tribe's economy. Use this if the worker is produced by a production site
244- rather than the warehouses. For example, ``default_target_quantity = 10``
245-
246- **experience**
247- *Optional*. The amount of experience that the worker needs to gather
248- in order to be transformend into a higher worker type. If `becomes`
249- is defined, this needs to be set as well.
250-
251- **becomes**
252- *Optional*. The name of the higher worker type that this worker will
253- transform to after gaining enough experience. If `experience`
254- is defined, this needs to be set as well.
255-
256- **animations**:
257- A table containing all animations for this worker.
258- Workers have an "idle" animation. They also have directional animations
259- called "walk" and "walkload" which are defined with the help of
260- :func:`add_walking_animations`, plus additional :ref:`animations` used in their
261- worker programs. The "idle" and "walk" animations are mandatory.
262-
263- **programs**:
264- *Optional*. If the worker leaves the building to do his work, the :ref:`tribes_worker_programs` that define which type of space or resource the worker has to find
265- on the map in order to do his work, and what that work is, including any
266- animations and sounds played.
267-
268- **ware_hotspot**
269- *Optional*. The x, y coordinates for adjusting the placement of the
270- ware being carried. The default value is ``{0, 15}``. Increase ``x``
271- to shift the ware to the left and ``y`` to shift it upwards. For example::
272-
273- ware_hotspot = { -2, 13 },
274+**msgctxt**: The context that Gettext will use to disambiguate the
275+translations for strings in this table.
276+
277+**name**: A string containing the internal name of this worker.
278+
279+**descname**: The translatable display name. Use ``pgettext`` with the
280+``msgctxt`` above to fetch the string.
281+
282+**helptext_script**: The full path to the ``helptexts.lua`` script for this worker.
283+
284+**icon**: The full path to the menu icon for this worker.
285+
286+**vision_range**
287+ The size of the radius that the worker sees.
288+
289+**buildcost**
290+ *Optional*. A table with the wares and workers used by warehouses to
291+ create this worker, containing warename - amount pairs, e.g.::
292+
293+ buildcost = { atlanteans_carrier = 1, hammer = 1 }
294+
295+**default_target_quantity**:
296+ *Optional*. An int defining the default target quantity for the worker's
297+ tribe's economy. Use this if the worker is produced by a production site
298+ rather than the warehouses. For example, ``default_target_quantity = 10``
299+
300+**experience**
301+ *Optional*. The amount of experience that the worker needs to gather
302+ in order to be transformend into a higher worker type. If `becomes`
303+ is defined, this needs to be set as well.
304+
305+**becomes**
306+ *Optional*. The name of the higher worker type that this worker will
307+ transform to after gaining enough experience. If `experience`
308+ is defined, this needs to be set as well.
309+
310+**animations**:
311+ A table containing all animations for this worker.
312+ Workers have an "idle" animation. They also have directional animations
313+ called "walk" and "walkload" which are defined with the help of
314+ :func:`add_walking_animations`, plus additional :ref:`animations` used in their
315+ worker programs. The "idle" and "walk" animations are mandatory.
316+
317+**programs**:
318+ *Optional*. If the worker leaves the building to do his work, the
319+ :ref:`tribes_worker_programs` that define which type of space or resource
320+ the worker has to find on the map in order to do his work, and what that
321+ work is, including any animations and sounds played.
322+
323+**ware_hotspot**
324+ *Optional*. The x, y coordinates for adjusting the placement of the
325+ ware being carried. The default value is ``{0, 15}``. Increase ``x``
326+ to shift the ware to the left and ``y`` to shift it upwards. For example::
327+
328+ ware_hotspot = { -2, 13 },
329+
330+**aihints**
331+ *Optional*. A list of hints for the AI. Can contain the following optional entries:
332+
333+ **preciousness**: How precious this worker is to each tribe. For example,
334+ ``{ atlanteans = 0, empire = 1 }``. You can use this for workers that are recruited.
335+
336
337 .. _lua_tribes_workers_helptexts:
338
339
340=== modified file 'src/ai/ai_help_structs.h'
341--- src/ai/ai_help_structs.h 2019-05-05 14:05:07 +0000
342+++ src/ai/ai_help_structs.h 2019-05-19 16:18:33 +0000
343@@ -477,6 +477,7 @@
344 bool requires_supporters;
345
346 // information needed for decision on new building construction
347+ int16_t initial_preciousness;
348 int16_t max_preciousness;
349 int16_t max_needed_preciousness;
350
351
352=== modified file 'src/ai/ai_hints.cc'
353--- src/ai/ai_hints.cc 2019-02-23 11:00:49 +0000
354+++ src/ai/ai_hints.cc 2019-05-19 16:18:33 +0000
355@@ -223,7 +223,6 @@
356 BuildingHints::BuildingHints(std::unique_ptr<LuaTable> table)
357 : mines_(table->has_key("mines") ? table->get_string("mines") : ""),
358 needs_water_(table->has_key("needs_water") ? table->get_bool("needs_water") : false),
359- recruitment_(table->has_key("recruitment") ? table->get_bool("recruitment") : false),
360 space_consumer_(table->has_key("space_consumer") ? table->get_bool("space_consumer") : false),
361 expansion_(table->has_key("expansion") ? table->get_bool("expansion") : false),
362 fighting_(table->has_key("fighting") ? table->get_bool("fighting") : false),
363@@ -274,3 +273,28 @@
364 }
365 NEVER_HERE();
366 }
367+
368+
369+// TODO(GunChleoc): WareDescr has a bare "preciousness" table that should be moved below a new "aihints" table.
370+void WareWorkerHints::read_preciousness(const LuaTable& table) {
371+ for (const std::string& key : table.keys<std::string>()) {
372+ preciousnesses_.insert(std::make_pair(key, table.get_int(key)));
373+ }
374+}
375+
376+/// Returns the preciousness of the ware, or kInvalidWare if the tribe doesn't use the ware.
377+int WareWorkerHints::preciousness(const std::string& tribename) const {
378+ if (preciousnesses_.count(tribename) > 0) {
379+ return preciousnesses_.at(tribename);
380+ }
381+ return Widelands::kInvalidWare;
382+}
383+
384+WareHints::WareHints(const LuaTable& table) : WareWorkerHints() {
385+ read_preciousness(table);
386+}
387+
388+
389+WorkerHints::WorkerHints(const LuaTable& table) : WareWorkerHints() {
390+ read_preciousness(*table.get_table("preciousness"));
391+}
392
393=== modified file 'src/ai/ai_hints.h'
394--- src/ai/ai_hints.h 2019-02-23 11:00:49 +0000
395+++ src/ai/ai_hints.h 2019-05-19 16:18:33 +0000
396@@ -23,9 +23,11 @@
397 #include <memory>
398 #include <stdint.h>
399 #include <string>
400+#include <unordered_map>
401
402 #include "base/log.h"
403 #include "base/macros.h"
404+#include "logic/widelands.h"
405 #include "scripting/lua_table.h"
406
407 namespace Widelands {
408@@ -56,10 +58,6 @@
409 return needs_water_;
410 }
411
412- bool for_recruitment() const {
413- return recruitment_;
414- }
415-
416 bool is_space_consumer() const {
417 return space_consumer_;
418 }
419@@ -110,7 +108,6 @@
420 private:
421 const std::string mines_;
422 const bool needs_water_;
423- const bool recruitment_;
424 const bool space_consumer_;
425 const bool expansion_;
426 const bool fighting_;
427@@ -131,4 +128,29 @@
428 DISALLOW_COPY_AND_ASSIGN(BuildingHints);
429 };
430
431+/// Hints common to wares and workers
432+struct WareWorkerHints {
433+ WareWorkerHints() = default;
434+
435+ /// Returns the preciousness of the ware/worker, or kInvalidWare if the tribe doesn't use the ware/worker or the worker has no preciousness defined for the tribe.
436+ int preciousness(const std::string& tribename) const;
437+
438+protected:
439+ void read_preciousness(const LuaTable& table);
440+
441+private:
442+ // tribename, preciousness. No default.
443+ std::unordered_map<std::string, int> preciousnesses_;
444+};
445+
446+/// Hints for wares
447+struct WareHints : WareWorkerHints {
448+ explicit WareHints(const LuaTable& table);
449+};
450+
451+/// Hints for workers
452+struct WorkerHints : WareWorkerHints {
453+ explicit WorkerHints(const LuaTable& table);
454+};
455+
456 #endif // end of include guard: WL_AI_AI_HINTS_H
457
458=== modified file 'src/ai/defaultai.cc'
459--- src/ai/defaultai.cc 2019-05-17 11:45:39 +0000
460+++ src/ai/defaultai.cc 2019-05-19 16:18:33 +0000
461@@ -52,7 +52,7 @@
462 #include "logic/player.h"
463 #include "logic/playercommand.h"
464
465-// following is in miliseconds (widelands counts time in ms)
466+// following is in milliseconds (widelands counts time in ms)
467 constexpr int kFieldInfoExpiration = 12 * 1000;
468 constexpr int kMineFieldInfoExpiration = 20 * 1000;
469 constexpr int kNewMineConstInterval = 19000;
470@@ -517,7 +517,7 @@
471 for (DescriptionIndex i = 0; i < static_cast<DescriptionIndex>(game().tribes().nrwares()); ++i) {
472 wares.at(i).producers = 0;
473 wares.at(i).consumers = 0;
474- wares.at(i).preciousness = game().tribes().get_ware_descr(i)->preciousness(tribe_->name());
475+ wares.at(i).preciousness = game().tribes().get_ware_descr(i)->ai_hints().preciousness(tribe_->name());
476 }
477
478 const DescriptionIndex& nr_buildings = game().tribes().nrbuildings();
479@@ -637,9 +637,6 @@
480 if (bh.is_space_consumer()) {
481 bo.set_is(BuildingAttribute::kSpaceConsumer);
482 }
483- if (bh.for_recruitment()) {
484- bo.set_is(BuildingAttribute::kRecruitment);
485- }
486 bo.expansion_type = bh.is_expansion_type();
487 bo.fighting_type = bh.is_fighting_type();
488 bo.mountain_conqueror = bh.is_mountain_conqueror();
489@@ -658,6 +655,7 @@
490 bo.set_is(BuildingAttribute::kPort);
491 }
492 bo.max_trainingsites_proportion = 100;
493+ bo.initial_preciousness = 0;
494 bo.max_preciousness = 0;
495 bo.max_needed_preciousness = 0;
496
497@@ -684,6 +682,26 @@
498 for (const DescriptionIndex& temp_output : prod.output_ware_types()) {
499 bo.outputs.push_back(temp_output);
500 }
501+
502+ // Read information about worker outputs
503+ if (prod.output_worker_types().size() > 0) {
504+ for (const DescriptionIndex& temp_output : prod.output_worker_types()) {
505+ if (temp_output == tribe_->soldier()) {
506+ bo.set_is(BuildingAttribute::kBarracks);
507+ }
508+ const WorkerHints* worker_hints = tribe_->get_worker_descr(temp_output)->ai_hints();
509+ if (worker_hints != nullptr) {
510+ const int worker_preciousness = worker_hints->preciousness(tribe_->name());
511+ if (worker_preciousness != Widelands::kInvalidWare) {
512+ bo.initial_preciousness += worker_preciousness;
513+ }
514+ }
515+ }
516+ if (!bo.is(BuildingAttribute::kBarracks) && bo.outputs.empty()) {
517+ bo.set_is(BuildingAttribute::kRecruitment);
518+ }
519+ }
520+
521 for (const auto& temp_position : prod.working_positions()) {
522 bo.positions.push_back(temp_position.first);
523 }
524@@ -719,9 +737,6 @@
525 if (bh.is_shipyard()) {
526 bo.set_is(BuildingAttribute::kShipyard);
527 }
528- if (building_index == tribe_->barracks()) {
529- bo.set_is(BuildingAttribute::kBarracks);
530- }
531 // Identify refined log producer
532 if (bo.outputs.size() == 1 && bo.outputs[0] == tribe_->refinedlog()) {
533 bo.set_is(BuildingAttribute::kLogRefiner);
534@@ -2489,20 +2504,23 @@
535
536 // Now verifying that all 'buildable' buildings has positive max_needed_preciousness
537 // if they have outputs, all other must have zero max_needed_preciousness
538- if ((bo.new_building == BuildingNecessity::kNeeded ||
539- bo.new_building == BuildingNecessity::kForced ||
540- bo.new_building == BuildingNecessity::kAllowed ||
541- bo.new_building == BuildingNecessity::kNeededPending) &&
542- (!bo.outputs.empty() || bo.is(BuildingAttribute::kBarracks))) {
543+
544+ if (bo.new_building == BuildingNecessity::kForbidden) {
545+ bo.max_needed_preciousness = 0;
546+ } else if ((bo.new_building == BuildingNecessity::kNeeded ||
547+ bo.new_building == BuildingNecessity::kForced ||
548+ bo.new_building == BuildingNecessity::kAllowed ||
549+ bo.new_building == BuildingNecessity::kNeededPending) &&
550+ (!bo.outputs.empty() || bo.initial_preciousness > 0)) { // bo.initial_preciousness signals that we have a worker output
551+ bo.max_needed_preciousness = std::max(bo.max_needed_preciousness, bo.initial_preciousness);
552+ bo.max_preciousness = std::max(bo.max_preciousness, bo.initial_preciousness);
553+
554 if (bo.max_needed_preciousness <= 0) {
555- throw wexception("AI: Max presciousness must not be <= 0 for building: %s",
556- bo.desc->name().c_str());
557+ throw wexception("AI: Max preciousness must not be <= 0 for building: %s",
558+ bo.desc->name().c_str());
559 }
560- } else if (bo.new_building == BuildingNecessity::kForbidden) {
561- bo.max_needed_preciousness = 0;
562 } else {
563 // For other situations we make sure max_needed_preciousness is zero
564-
565 assert(bo.max_needed_preciousness == 0);
566 }
567
568@@ -2965,7 +2983,6 @@
569 }
570
571 } else if (bo.is(BuildingAttribute::kRecruitment)) {
572- bo.max_needed_preciousness = 2;
573 prio += bo.primary_priority;
574 prio -= bf->unowned_land_nearby * 2;
575 prio -= (bf->enemy_nearby) * 100;
576@@ -4487,7 +4504,7 @@
577 gametime &&
578 site.site->can_start_working() &&
579 get_stocklevel(*site.bo, gametime) >
580- (std::abs(management_data.get_military_number_at(168)) / 5)) {
581+ static_cast<unsigned int>((std::abs(management_data.get_military_number_at(168)) / 5))) {
582
583 if (connected_to_wh) {
584 game().send_player_dismantle(*site.site);
585@@ -4980,42 +4997,41 @@
586 // Let deal with productionsites now
587 // First we iterate over outputs of building, count warehoused stock
588 // and deciding if we have enough on stock (in warehouses)
589- bo.max_preciousness = 0;
590- bo.max_needed_preciousness = 0;
591-
592- if (!bo.is(BuildingAttribute::kBarracks)) { // barracks are now excluded from calculation
593- // preciousness is assigned below in this fuction
594- for (uint32_t m = 0; m < bo.outputs.size(); ++m) {
595- DescriptionIndex wt(static_cast<size_t>(bo.outputs.at(m)));
596-
597- uint16_t target = tribe_->get_ware_descr(wt)->default_target_quantity(tribe_->name());
598- if (target == Widelands::kInvalidWare) {
599- target = kTargetQuantCap;
600- }
601- target /= 3;
602-
603- // at least 1
604- target = std::max<uint16_t>(target, 1);
605-
606- // it seems there are wares with 0 preciousness (no entry in init files?), but we need
607- // positive value here.
608- const uint16_t preciousness =
609- std::max<uint16_t>(wares.at(bo.outputs.at(m)).preciousness, 1);
610-
611- if (calculate_stocklevel(wt) < target ||
612- site_needed_for_economy == BasicEconomyBuildingStatus::kEncouraged) {
613- if (bo.max_needed_preciousness < preciousness) {
614- bo.max_needed_preciousness = preciousness;
615- }
616- if (site_needed_for_economy == BasicEconomyBuildingStatus::kEncouraged) {
617- bo.max_needed_preciousness +=
618- std::abs(management_data.get_military_number_at(144)) / 10;
619- }
620- }
621-
622- if (bo.max_preciousness < preciousness) {
623- bo.max_preciousness = preciousness;
624- }
625+
626+ // Calulate preciousness
627+ bo.max_preciousness = bo.initial_preciousness;
628+ bo.max_needed_preciousness = bo.initial_preciousness;
629+ for (uint32_t m = 0; m < bo.outputs.size(); ++m) {
630+ DescriptionIndex wt(static_cast<size_t>(bo.outputs.at(m)));
631+
632+ uint16_t target = tribe_->get_ware_descr(wt)->default_target_quantity(tribe_->name());
633+ if (target == Widelands::kInvalidWare) {
634+ target = kTargetQuantCap;
635+ }
636+ target /= 3;
637+
638+ // at least 1
639+ target = std::max<uint16_t>(target, 1);
640+
641+ // it seems there are wares with 0 preciousness (no entry in init files?), but we need
642+ // positive value here.
643+ // TODO(GunChleoc): Since we require in Tribes::postload() that this is set for all wares used by a tribe, something seems to be wrong here. It should always be > 0.
644+ const uint16_t preciousness =
645+ std::max<uint16_t>(wares.at(bo.outputs.at(m)).preciousness, 1);
646+
647+ if (calculate_stocklevel(wt) < target ||
648+ site_needed_for_economy == BasicEconomyBuildingStatus::kEncouraged) {
649+ if (bo.max_needed_preciousness < preciousness) {
650+ bo.max_needed_preciousness = preciousness;
651+ }
652+ if (site_needed_for_economy == BasicEconomyBuildingStatus::kEncouraged) {
653+ bo.max_needed_preciousness +=
654+ std::abs(management_data.get_military_number_at(144)) / 10;
655+ }
656+ }
657+
658+ if (bo.max_preciousness < preciousness) {
659+ bo.max_preciousness = preciousness;
660 }
661 }
662
663@@ -5102,12 +5118,7 @@
664 }
665
666 if (bo.forced_after < gametime && bo.total_count() == 0 && !has_substitution_building) {
667- if (!bo.is(BuildingAttribute::kBarracks)) {
668- bo.max_needed_preciousness = bo.max_preciousness;
669- } else {
670- // barracks has no genuine preciousness as of now
671- bo.max_needed_preciousness = 5;
672- }
673+ bo.max_needed_preciousness = bo.max_preciousness;
674 return BuildingNecessity::kForced;
675 } else if (bo.prohibited_till > gametime) {
676 return BuildingNecessity::kForbidden;
677@@ -5417,7 +5428,8 @@
678 bo.max_preciousness = bo.max_needed_preciousness;
679 return BuildingNecessity::kNeeded;
680 } else {
681- assert(bo.max_needed_preciousness == 0);
682+ bo.max_needed_preciousness = 0;
683+ bo.max_preciousness = 0;
684 return BuildingNecessity::kForbidden;
685 }
686 } else if (bo.type == BuildingObserver::Type::kMine) {
687@@ -5829,7 +5841,7 @@
688 return BuildingNecessity::kNeeded;
689 } else if (bo.inputs.size() == 1 &&
690 calculate_stocklevel(static_cast<size_t>(bo.inputs.at(0))) >
691- std::abs(management_data.get_military_number_at(171))) {
692+ static_cast<unsigned int>(std::abs(management_data.get_military_number_at(171)))) {
693 return BuildingNecessity::kNeeded;
694 } else {
695 return BuildingNecessity::kNotNeeded;
696
697=== modified file 'src/logic/map_objects/tribes/tribe_descr.cc'
698--- src/logic/map_objects/tribes/tribe_descr.cc 2019-05-04 10:47:44 +0000
699+++ src/logic/map_objects/tribes/tribe_descr.cc 2019-05-19 16:18:33 +0000
700@@ -199,7 +199,6 @@
701 }
702
703 port_ = add_special_building(table.get_string("port"));
704- barracks_ = add_special_building(table.get_string("barracks"));
705
706 ironore_ = add_special_ware(table.get_string("ironore"));
707 rawlog_ = add_special_ware(table.get_string("rawlog"));
708@@ -328,10 +327,6 @@
709 assert(tribes_.building_exists(port_));
710 return port_;
711 }
712-DescriptionIndex TribeDescr::barracks() const {
713- assert(tribes_.building_exists(barracks_));
714- return barracks_;
715-}
716 DescriptionIndex TribeDescr::ironore() const {
717 assert(tribes_.ware_exists(ironore_));
718 return ironore_;
719
720=== modified file 'src/logic/map_objects/tribes/tribe_descr.h'
721--- src/logic/map_objects/tribes/tribe_descr.h 2019-03-01 04:19:53 +0000
722+++ src/logic/map_objects/tribes/tribe_descr.h 2019-05-19 16:18:33 +0000
723@@ -116,7 +116,6 @@
724 DescriptionIndex soldier() const;
725 DescriptionIndex ship() const;
726 DescriptionIndex port() const;
727- DescriptionIndex barracks() const;
728 DescriptionIndex ironore() const;
729 DescriptionIndex rawlog() const;
730 DescriptionIndex refinedlog() const;
731@@ -206,7 +205,6 @@
732 DescriptionIndex soldier_; // The soldier that this tribe uses
733 DescriptionIndex ship_; // The ship that this tribe uses
734 DescriptionIndex port_; // The port that this tribe uses
735- DescriptionIndex barracks_; // The barracks to create soldiers
736 DescriptionIndex ironore_; // Iron ore
737 DescriptionIndex rawlog_; // Simple log
738 DescriptionIndex refinedlog_; // Refined log, e.g. wood or blackwood
739
740=== modified file 'src/logic/map_objects/tribes/tribes.cc'
741--- src/logic/map_objects/tribes/tribes.cc 2019-05-16 09:15:03 +0000
742+++ src/logic/map_objects/tribes/tribes.cc 2019-05-19 16:18:33 +0000
743@@ -357,7 +357,7 @@
744
745 // Verify that the preciousness has been set for all of the tribe's wares
746 for (const DescriptionIndex wi : tribe_descr->wares()) {
747- if (tribe_descr->get_ware_descr(wi)->preciousness(tribe_descr->name()) == kInvalidWare) {
748+ if (tribe_descr->get_ware_descr(wi)->ai_hints().preciousness(tribe_descr->name()) == kInvalidWare) {
749 throw GameDataError("The ware '%s' needs to define a preciousness for tribe '%s'",
750 tribe_descr->get_ware_descr(wi)->name().c_str(),
751 tribe_descr->name().c_str());
752
753=== modified file 'src/logic/map_objects/tribes/ware_descr.cc'
754--- src/logic/map_objects/tribes/ware_descr.cc 2019-02-23 11:00:49 +0000
755+++ src/logic/map_objects/tribes/ware_descr.cc 2019-05-19 16:18:33 +0000
756@@ -35,7 +35,8 @@
757 * /data/tribes/wares/armor/init.lua
758 */
759 WareDescr::WareDescr(const std::string& init_descname, const LuaTable& table)
760- : MapObjectDescr(MapObjectType::WARE, table.get_string("name"), init_descname, table) {
761+ : MapObjectDescr(MapObjectType::WARE, table.get_string("name"), init_descname, table),
762+ ai_hints_(new WareHints(*table.get_table("preciousness"))) {
763 if (helptext_script().empty()) {
764 throw GameDataError("Ware %s has no helptext script", name().c_str());
765 }
766@@ -51,19 +52,9 @@
767 for (const std::string& key : items_table->keys<std::string>()) {
768 default_target_quantities_.emplace(key, items_table->get_int(key));
769 }
770-
771- items_table = table.get_table("preciousness");
772- for (const std::string& key : items_table->keys<std::string>()) {
773- preciousnesses_.emplace(key, items_table->get_int(key));
774- }
775-}
776-
777-int WareDescr::preciousness(const std::string& tribename) const {
778- if (preciousnesses_.count(tribename) > 0) {
779- return preciousnesses_.at(tribename);
780- }
781- return kInvalidWare;
782-}
783+}
784+
785+
786
787 DescriptionIndex WareDescr::default_target_quantity(const std::string& tribename) const {
788 if (default_target_quantities_.count(tribename) > 0) {
789
790=== modified file 'src/logic/map_objects/tribes/ware_descr.h'
791--- src/logic/map_objects/tribes/ware_descr.h 2019-02-27 19:00:36 +0000
792+++ src/logic/map_objects/tribes/ware_descr.h 2019-05-19 16:18:33 +0000
793@@ -21,12 +21,14 @@
794 #define WL_LOGIC_MAP_OBJECTS_TRIBES_WARE_DESCR_H
795
796 #include <cstring>
797+#include <memory>
798 #include <string>
799 #include <unordered_map>
800
801 #include <stdint.h>
802
803 #include "base/macros.h"
804+#include "ai/ai_hints.h"
805 #include "logic/map_objects/map_object.h"
806 #include "scripting/lua_table.h"
807
808@@ -52,9 +54,10 @@
809 ~WareDescr() override {
810 }
811
812- /// Returns the preciousness of the ware, or kInvalidWare if the tribe doesn't use the ware.
813- /// It is used by the computer player.
814- int preciousness(const std::string& tribename) const;
815+ /// AI hints for this ware type
816+ const WareHints& ai_hints() const {
817+ return *ai_hints_;
818+ }
819
820 /// How much of the ware type an economy should store in warehouses.
821 /// The special value kInvalidWare means that the target quantity of this ware type will never be
822@@ -82,8 +85,9 @@
823 private:
824 // tribename, quantity. No default.
825 std::unordered_map<std::string, int> default_target_quantities_;
826- // tribename, preciousness. No default.
827- std::unordered_map<std::string, int> preciousnesses_;
828+
829+ // Hints for the AI
830+ std::unique_ptr<WareHints> ai_hints_;
831
832 std::set<DescriptionIndex> consumers_; // Buildings that consume this ware
833 std::set<DescriptionIndex> producers_; // Buildings that produce this ware
834
835=== modified file 'src/logic/map_objects/tribes/worker_descr.cc'
836--- src/logic/map_objects/tribes/worker_descr.cc 2019-05-05 14:05:07 +0000
837+++ src/logic/map_objects/tribes/worker_descr.cc 2019-05-19 16:18:33 +0000
838@@ -52,6 +52,7 @@
839 becomes_(table.has_key("experience") ? tribes.safe_worker_index(table.get_string("becomes")) :
840 INVALID_INDEX),
841 needed_experience_(table.has_key("becomes") ? table.get_int("experience") : INVALID_INDEX),
842+ ai_hints_(table.has_key("aihints") ? new WorkerHints(*table.get_table("aihints")) : nullptr),
843 tribes_(tribes) {
844 if (helptext_script().empty()) {
845 throw GameDataError("Worker %s has no helptext script", name().c_str());
846
847=== modified file 'src/logic/map_objects/tribes/worker_descr.h'
848--- src/logic/map_objects/tribes/worker_descr.h 2019-05-11 13:48:12 +0000
849+++ src/logic/map_objects/tribes/worker_descr.h 2019-05-19 16:18:33 +0000
850@@ -22,6 +22,7 @@
851
852 #include <memory>
853
854+#include "ai/ai_hints.h"
855 #include "base/macros.h"
856 #include "graphic/diranimations.h"
857 #include "logic/map_objects/bob.h"
858@@ -116,6 +117,11 @@
859 return programs_;
860 }
861
862+ /// AI hints for this worker type. Can be nullptr.
863+ const WorkerHints* ai_hints() const {
864+ return ai_hints_.get();
865+ }
866+
867 protected:
868 Programs programs_;
869
870@@ -145,6 +151,9 @@
871 std::set<DescriptionIndex> employers_;
872
873 private:
874+ // Hints for the AI
875+ std::unique_ptr<WorkerHints> ai_hints_;
876+
877 const Tribes& tribes_;
878 DISALLOW_COPY_AND_ASSIGN(WorkerDescr);
879 };

Subscribers

People subscribed via source and target branches

to status/vote changes: