← Back to team overview

widelands-dev team mailing list archive

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

 

GunChleoc has proposed merging lp:~widelands-dev/widelands/bug-1829471-worker-preciousness into lp:widelands.

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.


Requested reviews:
  Widelands Developers (widelands-dev)
Related bugs:
  Bug #1829471 in widelands: "Shift hardcoded AI values to lua"
  https://bugs.launchpad.net/widelands/+bug/1829471

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/bug-1829471-worker-preciousness/+merge/367608

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?
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/bug-1829471-worker-preciousness into lp:widelands.
=== modified file 'data/tribes/atlanteans.lua'
--- data/tribes/atlanteans.lua	2018-07-29 13:49:09 +0000
+++ data/tribes/atlanteans.lua	2019-05-18 13:37:09 +0000
@@ -375,7 +375,6 @@
    soldier = "atlanteans_soldier",
    ship = "atlanteans_ship",
    port = "atlanteans_port",
-   barracks = "atlanteans_barracks",
    ironore = "iron_ore",
    rawlog = "log",
    refinedlog = "planks",

=== modified file 'data/tribes/barbarians.lua'
--- data/tribes/barbarians.lua	2018-07-29 13:49:09 +0000
+++ data/tribes/barbarians.lua	2019-05-18 13:37:09 +0000
@@ -308,7 +308,6 @@
    soldier = "barbarians_soldier",
    ship = "barbarians_ship",
    port = "barbarians_port",
-   barracks = "barbarians_barracks",
    ironore = "iron_ore",
    rawlog = "log",
    refinedlog = "blackwood",

=== modified file 'data/tribes/buildings/productionsites/atlanteans/horsefarm/init.lua'
--- data/tribes/buildings/productionsites/atlanteans/horsefarm/init.lua	2019-05-15 06:29:24 +0000
+++ data/tribes/buildings/productionsites/atlanteans/horsefarm/init.lua	2019-05-18 13:37:09 +0000
@@ -31,7 +31,6 @@
    },
 
    aihints = {
-      recruitment = true,
       prohibited_till = 610,
    },
 

=== modified file 'data/tribes/buildings/productionsites/barbarians/cattlefarm/init.lua'
--- data/tribes/buildings/productionsites/barbarians/cattlefarm/init.lua	2019-05-15 06:29:24 +0000
+++ data/tribes/buildings/productionsites/barbarians/cattlefarm/init.lua	2019-05-18 13:37:09 +0000
@@ -31,7 +31,6 @@
    },
 
    aihints = {
-      recruitment = true,
       prohibited_till = 610,
    },
 

=== modified file 'data/tribes/buildings/productionsites/empire/donkeyfarm/init.lua'
--- data/tribes/buildings/productionsites/empire/donkeyfarm/init.lua	2019-05-15 06:29:24 +0000
+++ data/tribes/buildings/productionsites/empire/donkeyfarm/init.lua	2019-05-18 13:37:09 +0000
@@ -31,7 +31,6 @@
    },
 
    aihints = {
-      recruitment = true,
       prohibited_till = 610,
    },
 

=== modified file 'data/tribes/empire.lua'
--- data/tribes/empire.lua	2018-07-29 13:49:09 +0000
+++ data/tribes/empire.lua	2019-05-18 13:37:09 +0000
@@ -350,7 +350,6 @@
    soldier = "empire_soldier",
    ship = "empire_ship",
    port = "empire_port",
-   barracks = "empire_barracks",
    ironore = "iron_ore",
    rawlog = "log",
    refinedlog = "planks",

=== modified file 'data/tribes/frisians.lua'
--- data/tribes/frisians.lua	2019-02-28 11:03:51 +0000
+++ data/tribes/frisians.lua	2019-05-18 13:37:09 +0000
@@ -349,7 +349,6 @@
    soldier = "frisians_soldier",
    ship = "frisians_ship",
    port = "frisians_port",
-   barracks = "frisians_barracks",
    ironore = "iron_ore",
    rawlog = "log",
    refinedlog = "brick",

=== modified file 'data/tribes/workers/atlanteans/horse/init.lua'
--- data/tribes/workers/atlanteans/horse/init.lua	2017-02-12 09:10:57 +0000
+++ data/tribes/workers/atlanteans/horse/init.lua	2019-05-18 13:37:09 +0000
@@ -24,4 +24,10 @@
    ware_hotspot = {-2, 12},
 
    animations = animations,
+
+   aihints = {
+      preciousness = {
+         atlanteans = 2
+      },
+   }
 }

=== modified file 'data/tribes/workers/barbarians/ox/init.lua'
--- data/tribes/workers/barbarians/ox/init.lua	2017-02-12 09:10:57 +0000
+++ data/tribes/workers/barbarians/ox/init.lua	2019-05-18 13:37:09 +0000
@@ -25,4 +25,10 @@
    ware_hotspot = { -2, 13 },
 
    animations = animations,
+
+   aihints = {
+      preciousness = {
+         barbarians = 2
+      },
+   }
 }

=== modified file 'data/tribes/workers/empire/donkey/init.lua'
--- data/tribes/workers/empire/donkey/init.lua	2017-02-12 09:10:57 +0000
+++ data/tribes/workers/empire/donkey/init.lua	2019-05-18 13:37:09 +0000
@@ -24,4 +24,10 @@
    ware_hotspot =  { -2, 8 },
 
    animations = animations,
+
+   aihints = {
+      preciousness = {
+         empire = 2
+      },
+   }
 }

=== modified file 'data/tribes/workers/frisians/reindeer/init.lua'
--- data/tribes/workers/frisians/reindeer/init.lua	2018-02-06 11:17:48 +0000
+++ data/tribes/workers/frisians/reindeer/init.lua	2019-05-18 13:37:09 +0000
@@ -24,4 +24,10 @@
    ware_hotspot = { 0, 18 },
 
    animations = animations,
+
+   aihints = {
+      preciousness = {
+         frisians = 2
+      },
+   }
 }

=== modified file 'doc/sphinx/source/lua_tribes_workers.rst.org'
--- doc/sphinx/source/lua_tribes_workers.rst.org	2018-11-30 10:36:42 +0000
+++ doc/sphinx/source/lua_tribes_workers.rst.org	2019-05-18 13:37:09 +0000
@@ -17,60 +17,68 @@
 
 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:
 
-  **msgctxt**: The context that Gettext will use to disambiguate the
-  translations for strings in this table.
-
-  **name**: A string containing the internal name of this worker.
-
-  **descname**: The translatable display name. Use ``pgettext`` with the
-  ``msgctxt`` above to fetch the string.
-
-  **helptext_script**: The full path to the ``helptexts.lua`` script for this worker.
-
-  **icon**: The full path to the menu icon for this worker.
-
-  **vision_range**
-      The size of the radius that the worker sees.
-
-  **buildcost**
-      *Optional*. A table with the wares and workers used by warehouses to
-      create this worker, containing warename - amount pairs, e.g.::
-
-          buildcost = { atlanteans_carrier = 1, hammer = 1 }
-
-  **default_target_quantity**:
-      *Optional*. An int defining the default target quantity for the worker's
-      tribe's economy. Use this if the worker is produced by a production site
-      rather than the warehouses. For example, ``default_target_quantity = 10``
-
-  **experience**
-      *Optional*. The amount of experience that the worker needs to gather
-      in order to be transformend into a higher worker type. If `becomes`
-      is defined, this needs to be set as well.
-
-  **becomes**
-      *Optional*. The name of the higher worker type that this worker will
-      transform to after gaining enough experience. If `experience`
-      is defined, this needs to be set as well.
-
-  **animations**:
-       A table containing all animations for this worker.
-       Workers have an "idle" animation. They also have directional animations
-       called "walk" and "walkload" which are defined with the help of
-       :func:`add_walking_animations`, plus additional :ref:`animations` used in their
-       worker programs. The "idle" and "walk" animations are mandatory.
-
-  **programs**:
-      *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
-      on the map in order to do his work, and what that work is, including any
-      animations and sounds played.
-
-  **ware_hotspot**
-      *Optional*. The x, y coordinates for adjusting the placement of the
-      ware being carried. The default value is ``{0, 15}``. Increase ``x``
-      to shift the ware to the left and ``y`` to shift it upwards. For example::
-
-          ware_hotspot = { -2, 13 },
+**msgctxt**: The context that Gettext will use to disambiguate the
+translations for strings in this table.
+
+**name**: A string containing the internal name of this worker.
+
+**descname**: The translatable display name. Use ``pgettext`` with the
+``msgctxt`` above to fetch the string.
+
+**helptext_script**: The full path to the ``helptexts.lua`` script for this worker.
+
+**icon**: The full path to the menu icon for this worker.
+
+**vision_range**
+    The size of the radius that the worker sees.
+
+**buildcost**
+    *Optional*. A table with the wares and workers used by warehouses to
+    create this worker, containing warename - amount pairs, e.g.::
+
+        buildcost = { atlanteans_carrier = 1, hammer = 1 }
+
+**default_target_quantity**:
+    *Optional*. An int defining the default target quantity for the worker's
+    tribe's economy. Use this if the worker is produced by a production site
+    rather than the warehouses. For example, ``default_target_quantity = 10``
+
+**experience**
+    *Optional*. The amount of experience that the worker needs to gather
+    in order to be transformend into a higher worker type. If `becomes`
+    is defined, this needs to be set as well.
+
+**becomes**
+    *Optional*. The name of the higher worker type that this worker will
+    transform to after gaining enough experience. If `experience`
+    is defined, this needs to be set as well.
+
+**animations**:
+     A table containing all animations for this worker.
+     Workers have an "idle" animation. They also have directional animations
+     called "walk" and "walkload" which are defined with the help of
+     :func:`add_walking_animations`, plus additional :ref:`animations` used in their
+     worker programs. The "idle" and "walk" animations are mandatory.
+
+**programs**:
+    *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 on the map in order to do his work, and what that
+    work is, including any animations and sounds played.
+
+**ware_hotspot**
+    *Optional*. The x, y coordinates for adjusting the placement of the
+    ware being carried. The default value is ``{0, 15}``. Increase ``x``
+    to shift the ware to the left and ``y`` to shift it upwards. For example::
+
+        ware_hotspot = { -2, 13 },
+
+**aihints**
+    *Optional*. A list of hints for the AI. Can contain the following optional entries:
+
+        **preciousness**: How precious this worker is to each tribe. For example,
+        ``{ atlanteans = 0, empire = 1 }``. You can use this for workers that are recruited.
+
 
 .. _lua_tribes_workers_helptexts:
 

=== modified file 'src/ai/ai_help_structs.h'
--- src/ai/ai_help_structs.h	2019-05-05 14:05:07 +0000
+++ src/ai/ai_help_structs.h	2019-05-18 13:37:09 +0000
@@ -477,6 +477,7 @@
 	bool requires_supporters;
 
 	// information needed for decision on new building construction
+	int16_t initial_preciousness;
 	int16_t max_preciousness;
 	int16_t max_needed_preciousness;
 

=== modified file 'src/ai/ai_hints.cc'
--- src/ai/ai_hints.cc	2019-02-23 11:00:49 +0000
+++ src/ai/ai_hints.cc	2019-05-18 13:37:09 +0000
@@ -223,7 +223,6 @@
 BuildingHints::BuildingHints(std::unique_ptr<LuaTable> table)
    : mines_(table->has_key("mines") ? table->get_string("mines") : ""),
      needs_water_(table->has_key("needs_water") ? table->get_bool("needs_water") : false),
-     recruitment_(table->has_key("recruitment") ? table->get_bool("recruitment") : false),
      space_consumer_(table->has_key("space_consumer") ? table->get_bool("space_consumer") : false),
      expansion_(table->has_key("expansion") ? table->get_bool("expansion") : false),
      fighting_(table->has_key("fighting") ? table->get_bool("fighting") : false),
@@ -274,3 +273,28 @@
 	}
 	NEVER_HERE();
 }
+
+
+// TODO(GunChleoc): WareDescr has a bare "preciousness" table that should be moved below a new "aihints" table.
+void WareWorkerHints::read_preciousness(const LuaTable& table) {
+	for (const std::string& key : table.keys<std::string>()) {
+		preciousnesses_.insert(std::make_pair(key, table.get_int(key)));
+	}
+}
+
+/// Returns the preciousness of the ware, or kInvalidWare if the tribe doesn't use the ware.
+int WareWorkerHints::preciousness(const std::string& tribename) const {
+	if (preciousnesses_.count(tribename) > 0) {
+		return preciousnesses_.at(tribename);
+	}
+	return Widelands::kInvalidWare;
+}
+
+WareHints::WareHints(const LuaTable& table) : WareWorkerHints() {
+	read_preciousness(table);
+}
+
+
+WorkerHints::WorkerHints(const LuaTable& table) : WareWorkerHints() {
+	read_preciousness(*table.get_table("preciousness"));
+}

=== modified file 'src/ai/ai_hints.h'
--- src/ai/ai_hints.h	2019-02-23 11:00:49 +0000
+++ src/ai/ai_hints.h	2019-05-18 13:37:09 +0000
@@ -23,9 +23,11 @@
 #include <memory>
 #include <stdint.h>
 #include <string>
+#include <unordered_map>
 
 #include "base/log.h"
 #include "base/macros.h"
+#include "logic/widelands.h"
 #include "scripting/lua_table.h"
 
 namespace Widelands {
@@ -56,10 +58,6 @@
 		return needs_water_;
 	}
 
-	bool for_recruitment() const {
-		return recruitment_;
-	}
-
 	bool is_space_consumer() const {
 		return space_consumer_;
 	}
@@ -110,7 +108,6 @@
 private:
 	const std::string mines_;
 	const bool needs_water_;
-	const bool recruitment_;
 	const bool space_consumer_;
 	const bool expansion_;
 	const bool fighting_;
@@ -131,4 +128,29 @@
 	DISALLOW_COPY_AND_ASSIGN(BuildingHints);
 };
 
+/// Hints common to wares and workers
+struct WareWorkerHints {
+	WareWorkerHints() = default;
+
+	/// 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.
+	int preciousness(const std::string& tribename) const;
+
+protected:
+	void read_preciousness(const LuaTable& table);
+
+private:
+	// tribename, preciousness. No default.
+	std::unordered_map<std::string, int> preciousnesses_;
+};
+
+/// Hints for wares
+struct WareHints : WareWorkerHints {
+	explicit WareHints(const LuaTable& table);
+};
+
+/// Hints for workers
+struct WorkerHints : WareWorkerHints {
+	explicit WorkerHints(const LuaTable& table);
+};
+
 #endif  // end of include guard: WL_AI_AI_HINTS_H

=== modified file 'src/ai/defaultai.cc'
--- src/ai/defaultai.cc	2019-05-17 11:45:39 +0000
+++ src/ai/defaultai.cc	2019-05-18 13:37:09 +0000
@@ -52,7 +52,7 @@
 #include "logic/player.h"
 #include "logic/playercommand.h"
 
-// following is in miliseconds (widelands counts time in ms)
+// following is in milliseconds (widelands counts time in ms)
 constexpr int kFieldInfoExpiration = 12 * 1000;
 constexpr int kMineFieldInfoExpiration = 20 * 1000;
 constexpr int kNewMineConstInterval = 19000;
@@ -517,7 +517,7 @@
 	for (DescriptionIndex i = 0; i < static_cast<DescriptionIndex>(game().tribes().nrwares()); ++i) {
 		wares.at(i).producers = 0;
 		wares.at(i).consumers = 0;
-		wares.at(i).preciousness = game().tribes().get_ware_descr(i)->preciousness(tribe_->name());
+		wares.at(i).preciousness = game().tribes().get_ware_descr(i)->ai_hints().preciousness(tribe_->name());
 	}
 
 	const DescriptionIndex& nr_buildings = game().tribes().nrbuildings();
@@ -637,9 +637,6 @@
 		if (bh.is_space_consumer()) {
 			bo.set_is(BuildingAttribute::kSpaceConsumer);
 		}
-		if (bh.for_recruitment()) {
-			bo.set_is(BuildingAttribute::kRecruitment);
-		}
 		bo.expansion_type = bh.is_expansion_type();
 		bo.fighting_type = bh.is_fighting_type();
 		bo.mountain_conqueror = bh.is_mountain_conqueror();
@@ -658,6 +655,7 @@
 			bo.set_is(BuildingAttribute::kPort);
 		}
 		bo.max_trainingsites_proportion = 100;
+		bo.initial_preciousness = 0;
 		bo.max_preciousness = 0;
 		bo.max_needed_preciousness = 0;
 
@@ -684,6 +682,21 @@
 			for (const DescriptionIndex& temp_output : prod.output_ware_types()) {
 				bo.outputs.push_back(temp_output);
 			}
+
+			// Read information about worker outputs
+			if (prod.output_worker_types().size() > 0) {
+				for (const DescriptionIndex& temp_output : prod.output_worker_types()) {
+					bo.set_is(temp_output == tribe_->soldier() ? BuildingAttribute::kBarracks : BuildingAttribute::kRecruitment);
+					const WorkerHints* worker_hints = tribe_->get_worker_descr(temp_output)->ai_hints();
+					if (worker_hints != nullptr) {
+						int worker_preciousness = worker_hints->preciousness(tribe_->name());
+						if (worker_preciousness != Widelands::kInvalidWare) {
+							bo.initial_preciousness += worker_preciousness;
+						}
+					}
+				}
+			}
+
 			for (const auto& temp_position : prod.working_positions()) {
 				bo.positions.push_back(temp_position.first);
 			}
@@ -719,9 +732,6 @@
 			if (bh.is_shipyard()) {
 				bo.set_is(BuildingAttribute::kShipyard);
 			}
-			if (building_index == tribe_->barracks()) {
-				bo.set_is(BuildingAttribute::kBarracks);
-			}
 			// Identify refined log producer
 			if (bo.outputs.size() == 1 && bo.outputs[0] == tribe_->refinedlog()) {
 				bo.set_is(BuildingAttribute::kLogRefiner);
@@ -2489,21 +2499,25 @@
 
 			// Now verifying that all 'buildable' buildings has positive max_needed_preciousness
 			// if they have outputs, all other must have zero max_needed_preciousness
-			if ((bo.new_building == BuildingNecessity::kNeeded ||
-			     bo.new_building == BuildingNecessity::kForced ||
-			     bo.new_building == BuildingNecessity::kAllowed ||
-			     bo.new_building == BuildingNecessity::kNeededPending) &&
-			    (!bo.outputs.empty() || bo.is(BuildingAttribute::kBarracks))) {
-				if (bo.max_needed_preciousness <= 0) {
-					throw wexception("AI: Max presciousness must not be <= 0 for building: %s",
-					                 bo.desc->name().c_str());
-				}
-			} else if (bo.new_building == BuildingNecessity::kForbidden) {
+
+			if (bo.new_building == BuildingNecessity::kForbidden) {
 				bo.max_needed_preciousness = 0;
 			} else {
-				// For other situations we make sure max_needed_preciousness is zero
-
-				assert(bo.max_needed_preciousness == 0);
+				bo.max_needed_preciousness = std::max(bo.max_needed_preciousness, bo.initial_preciousness);
+				bo.max_preciousness = std::max(bo.max_preciousness, bo.initial_preciousness);
+				if ((bo.new_building == BuildingNecessity::kNeeded ||
+					 bo.new_building == BuildingNecessity::kForced ||
+					 bo.new_building == BuildingNecessity::kAllowed ||
+					 bo.new_building == BuildingNecessity::kNeededPending) &&
+					(!bo.outputs.empty() || bo.is(BuildingAttribute::kBarracks))) {
+					if (bo.max_needed_preciousness <= 0) {
+						throw wexception("AI: Max presciousness must not be <= 0 for building: %s",
+										 bo.desc->name().c_str());
+					}
+				} else {
+					// For other situations we make sure max_needed_preciousness is zero
+					// NOCOM this fails for recruitment sites now assert(bo.max_needed_preciousness == 0);
+				}
 			}
 
 			// Positive max_needed_preciousness says a building type is needed
@@ -2965,7 +2979,6 @@
 					}
 
 				} else if (bo.is(BuildingAttribute::kRecruitment)) {
-					bo.max_needed_preciousness = 2;
 					prio += bo.primary_priority;
 					prio -= bf->unowned_land_nearby * 2;
 					prio -= (bf->enemy_nearby) * 100;
@@ -4487,7 +4500,7 @@
 		       gametime &&
 		    site.site->can_start_working() &&
 		    get_stocklevel(*site.bo, gametime) >
-		       (std::abs(management_data.get_military_number_at(168)) / 5)) {
+		      static_cast<unsigned int>((std::abs(management_data.get_military_number_at(168)) / 5))) {
 
 			if (connected_to_wh) {
 				game().send_player_dismantle(*site.site);
@@ -4980,11 +4993,14 @@
 	// Let deal with productionsites now
 	// First we iterate over outputs of building, count warehoused stock
 	// and deciding if we have enough on stock (in warehouses)
-	bo.max_preciousness = 0;
-	bo.max_needed_preciousness = 0;
-
-	if (!bo.is(BuildingAttribute::kBarracks)) {  // barracks are now excluded from calculation
-		// preciousness is assigned below in this fuction
+	if (bo.is(BuildingAttribute::kBarracks)) {
+		// Barracks are excluded from preciousness calculation
+		bo.max_preciousness = 0;
+		bo.max_needed_preciousness = 0;
+	} else {
+		// preciousness is assigned below in this function
+		bo.max_preciousness = bo.initial_preciousness;
+		bo.max_needed_preciousness = bo.initial_preciousness;
 		for (uint32_t m = 0; m < bo.outputs.size(); ++m) {
 			DescriptionIndex wt(static_cast<size_t>(bo.outputs.at(m)));
 
@@ -4999,6 +5015,7 @@
 
 			// it seems there are wares with 0 preciousness (no entry in init files?), but we need
 			// positive value here.
+			// 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.
 			const uint16_t preciousness =
 			   std::max<uint16_t>(wares.at(bo.outputs.at(m)).preciousness, 1);
 
@@ -5829,7 +5846,7 @@
 			return BuildingNecessity::kNeeded;
 		} else if (bo.inputs.size() == 1 &&
 		           calculate_stocklevel(static_cast<size_t>(bo.inputs.at(0))) >
-		              std::abs(management_data.get_military_number_at(171))) {
+		              static_cast<unsigned int>(std::abs(management_data.get_military_number_at(171)))) {
 			return BuildingNecessity::kNeeded;
 		} else {
 			return BuildingNecessity::kNotNeeded;

=== modified file 'src/logic/map_objects/tribes/tribe_descr.cc'
--- src/logic/map_objects/tribes/tribe_descr.cc	2019-05-04 10:47:44 +0000
+++ src/logic/map_objects/tribes/tribe_descr.cc	2019-05-18 13:37:09 +0000
@@ -199,7 +199,6 @@
 		}
 
 		port_ = add_special_building(table.get_string("port"));
-		barracks_ = add_special_building(table.get_string("barracks"));
 
 		ironore_ = add_special_ware(table.get_string("ironore"));
 		rawlog_ = add_special_ware(table.get_string("rawlog"));
@@ -328,10 +327,6 @@
 	assert(tribes_.building_exists(port_));
 	return port_;
 }
-DescriptionIndex TribeDescr::barracks() const {
-	assert(tribes_.building_exists(barracks_));
-	return barracks_;
-}
 DescriptionIndex TribeDescr::ironore() const {
 	assert(tribes_.ware_exists(ironore_));
 	return ironore_;

=== modified file 'src/logic/map_objects/tribes/tribe_descr.h'
--- src/logic/map_objects/tribes/tribe_descr.h	2019-03-01 04:19:53 +0000
+++ src/logic/map_objects/tribes/tribe_descr.h	2019-05-18 13:37:09 +0000
@@ -116,7 +116,6 @@
 	DescriptionIndex soldier() const;
 	DescriptionIndex ship() const;
 	DescriptionIndex port() const;
-	DescriptionIndex barracks() const;
 	DescriptionIndex ironore() const;
 	DescriptionIndex rawlog() const;
 	DescriptionIndex refinedlog() const;
@@ -206,7 +205,6 @@
 	DescriptionIndex soldier_;     // The soldier that this tribe uses
 	DescriptionIndex ship_;        // The ship that this tribe uses
 	DescriptionIndex port_;        // The port that this tribe uses
-	DescriptionIndex barracks_;    // The barracks to create soldiers
 	DescriptionIndex ironore_;     // Iron ore
 	DescriptionIndex rawlog_;      // Simple log
 	DescriptionIndex refinedlog_;  // Refined log, e.g. wood or blackwood

=== modified file 'src/logic/map_objects/tribes/tribes.cc'
--- src/logic/map_objects/tribes/tribes.cc	2019-05-16 09:15:03 +0000
+++ src/logic/map_objects/tribes/tribes.cc	2019-05-18 13:37:09 +0000
@@ -357,7 +357,7 @@
 
 		// Verify that the preciousness has been set for all of the tribe's wares
 		for (const DescriptionIndex wi : tribe_descr->wares()) {
-			if (tribe_descr->get_ware_descr(wi)->preciousness(tribe_descr->name()) == kInvalidWare) {
+			if (tribe_descr->get_ware_descr(wi)->ai_hints().preciousness(tribe_descr->name()) == kInvalidWare) {
 				throw GameDataError("The ware '%s' needs to define a preciousness for tribe '%s'",
 				                    tribe_descr->get_ware_descr(wi)->name().c_str(),
 				                    tribe_descr->name().c_str());

=== modified file 'src/logic/map_objects/tribes/ware_descr.cc'
--- src/logic/map_objects/tribes/ware_descr.cc	2019-02-23 11:00:49 +0000
+++ src/logic/map_objects/tribes/ware_descr.cc	2019-05-18 13:37:09 +0000
@@ -35,7 +35,8 @@
  * /data/tribes/wares/armor/init.lua
  */
 WareDescr::WareDescr(const std::string& init_descname, const LuaTable& table)
-   : MapObjectDescr(MapObjectType::WARE, table.get_string("name"), init_descname, table) {
+   : MapObjectDescr(MapObjectType::WARE, table.get_string("name"), init_descname, table),
+	 ai_hints_(new WareHints(*table.get_table("preciousness"))) {
 	if (helptext_script().empty()) {
 		throw GameDataError("Ware %s has no helptext script", name().c_str());
 	}
@@ -51,19 +52,9 @@
 	for (const std::string& key : items_table->keys<std::string>()) {
 		default_target_quantities_.emplace(key, items_table->get_int(key));
 	}
-
-	items_table = table.get_table("preciousness");
-	for (const std::string& key : items_table->keys<std::string>()) {
-		preciousnesses_.emplace(key, items_table->get_int(key));
-	}
-}
-
-int WareDescr::preciousness(const std::string& tribename) const {
-	if (preciousnesses_.count(tribename) > 0) {
-		return preciousnesses_.at(tribename);
-	}
-	return kInvalidWare;
-}
+}
+
+
 
 DescriptionIndex WareDescr::default_target_quantity(const std::string& tribename) const {
 	if (default_target_quantities_.count(tribename) > 0) {

=== modified file 'src/logic/map_objects/tribes/ware_descr.h'
--- src/logic/map_objects/tribes/ware_descr.h	2019-02-27 19:00:36 +0000
+++ src/logic/map_objects/tribes/ware_descr.h	2019-05-18 13:37:09 +0000
@@ -21,12 +21,14 @@
 #define WL_LOGIC_MAP_OBJECTS_TRIBES_WARE_DESCR_H
 
 #include <cstring>
+#include <memory>
 #include <string>
 #include <unordered_map>
 
 #include <stdint.h>
 
 #include "base/macros.h"
+#include "ai/ai_hints.h"
 #include "logic/map_objects/map_object.h"
 #include "scripting/lua_table.h"
 
@@ -52,9 +54,10 @@
 	~WareDescr() override {
 	}
 
-	/// Returns the preciousness of the ware, or kInvalidWare if the tribe doesn't use the ware.
-	/// It is used by the computer player.
-	int preciousness(const std::string& tribename) const;
+	/// AI hints for this ware type
+	const WareHints& ai_hints() const {
+		return *ai_hints_;
+	}
 
 	/// How much of the ware type an economy should store in warehouses.
 	/// The special value kInvalidWare means that the target quantity of this ware type will never be
@@ -82,8 +85,9 @@
 private:
 	// tribename, quantity. No default.
 	std::unordered_map<std::string, int> default_target_quantities_;
-	// tribename, preciousness. No default.
-	std::unordered_map<std::string, int> preciousnesses_;
+
+	// Hints for the AI
+	std::unique_ptr<WareHints> ai_hints_;
 
 	std::set<DescriptionIndex> consumers_;  // Buildings that consume this ware
 	std::set<DescriptionIndex> producers_;  // Buildings that produce this ware

=== modified file 'src/logic/map_objects/tribes/worker_descr.cc'
--- src/logic/map_objects/tribes/worker_descr.cc	2019-05-05 14:05:07 +0000
+++ src/logic/map_objects/tribes/worker_descr.cc	2019-05-18 13:37:09 +0000
@@ -52,6 +52,7 @@
      becomes_(table.has_key("experience") ? tribes.safe_worker_index(table.get_string("becomes")) :
                                             INVALID_INDEX),
      needed_experience_(table.has_key("becomes") ? table.get_int("experience") : INVALID_INDEX),
+	 ai_hints_(table.has_key("aihints") ? new WorkerHints(*table.get_table("aihints")) : nullptr),
      tribes_(tribes) {
 	if (helptext_script().empty()) {
 		throw GameDataError("Worker %s has no helptext script", name().c_str());

=== modified file 'src/logic/map_objects/tribes/worker_descr.h'
--- src/logic/map_objects/tribes/worker_descr.h	2019-03-01 04:19:53 +0000
+++ src/logic/map_objects/tribes/worker_descr.h	2019-05-18 13:37:09 +0000
@@ -22,6 +22,7 @@
 
 #include <memory>
 
+#include "ai/ai_hints.h"
 #include "base/macros.h"
 #include "graphic/diranimations.h"
 #include "logic/map_objects/bob.h"
@@ -116,6 +117,11 @@
 		return programs_;
 	}
 
+	/// AI hints for this worker type. Can be nullptr.
+	const WorkerHints* ai_hints() const {
+		return ai_hints_.get();
+	}
+
 protected:
 	Programs programs_;
 
@@ -145,6 +151,9 @@
 	std::set<DescriptionIndex> employers_;
 
 private:
+	// Hints for the AI
+	std::unique_ptr<WorkerHints> ai_hints_;
+
 	const Tribes& tribes_;
 	DISALLOW_COPY_AND_ASSIGN(WorkerDescr);
 };


Follow ups