← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/lua-driven-help into lp:widelands

 

GunChleoc has proposed merging lp:~widelands-dev/widelands/lua-driven-help into lp:widelands.

Commit message:
The contents of the Tribal Encyclopedia / Editor Help are now defined in Lua files. Removed Readme/Authors/License buttons from in-game/in-editor menu.

Requested reviews:
  Widelands Developers (widelands-dev)

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/lua-driven-help/+merge/289782

We can now write arbitrary help chapters in Lua.

When we have merged lp:~widelands-dev/widelands/about, we can improve the contents of the first tab.
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/lua-driven-help into lp:widelands.
=== added file 'data/scripting/editor/editor_help.lua'
--- data/scripting/editor/editor_help.lua	1970-01-01 00:00:00 +0000
+++ data/scripting/editor/editor_help.lua	2016-03-22 12:21:57 +0000
@@ -0,0 +1,77 @@
+-- Returns definitions for encyclopedia tabs and their contents for the
+-- Editor Help
+
+
+-- Comparison function used to sort map objects alphabetically
+function compare_by_title(a, b)
+  return a["title"] < b["title"]
+end
+
+-- Returns help entries for all the terrains in the world
+function get_terrains()
+   local result = {}
+   for i, terrain in ipairs(wl.World().terrain_descriptions) do
+      result[i] = {
+         name = terrain.name,
+         title = terrain.descname,
+         icon = terrain.representative_image,
+         script = "scripting/editor/terrain_help.lua",
+         script_parameters = {[1] = terrain.name}
+      }
+   end
+   table.sort(result, compare_by_title)
+   return result
+end
+
+-- Returns help entries for all the trees in the world
+function get_trees()
+   local result = {}
+   local counter = 1
+   for i, immovable in ipairs(wl.World().immovable_descriptions) do
+      if (immovable:has_attribute("tree")) then
+         result[counter] = {
+            name = immovable.name,
+            title = immovable.species,
+            icon = immovable.representative_image,
+            script = "scripting/editor/tree_help.lua",
+            script_parameters = {[1] = immovable.name}
+         }
+         counter = counter  + 1
+      end
+   end
+   table.sort(result, compare_by_title)
+   return result
+end
+
+-- Main function
+set_textdomain("widelands")
+return {
+   title = _"Editor Help",
+   tabs = {
+      {
+         name = "general",
+         title = _"General",
+         icon = "images/logos/WL-Editor-32.png",
+         entries = {
+            {
+               name = "intro",
+               title = _"Introduction",
+               script = "txts/editor_readme.lua",
+               script_parameters = {}
+            }
+         }
+      },
+      {
+         name = "terrains",
+         title = _"Terrains",
+         icon = "images/wui/editor/editor_menu_tool_set_terrain.png",
+         entries = get_terrains()
+      },
+      {
+         name = "trees",
+         title = _"Trees",
+         icon = "world/immovables/trees/alder/old/idle_0.png",
+         entries = get_trees()
+      }
+   }
+}

=== modified file 'data/scripting/editor/terrain_help.lua'
--- data/scripting/editor/terrain_help.lua	2016-02-13 13:21:48 +0000
+++ data/scripting/editor/terrain_help.lua	2016-03-22 12:21:57 +0000
@@ -71,6 +71,9 @@
       else
          result = result .. rt(p(_"No trees will grow here."))
       end
-      return result
+      return {
+         title = terrain.descname,
+         text = result
+      }
    end
 }

=== modified file 'data/scripting/editor/tree_help.lua'
--- data/scripting/editor/tree_help.lua	2016-02-13 13:21:48 +0000
+++ data/scripting/editor/tree_help.lua	2016-03-22 12:21:57 +0000
@@ -39,6 +39,9 @@
                "<br>" .. ("%2.1f%%"):bformat(100 * v.probability)
             ) .. spacer()
       end
-      return result
+      return {
+         title = tree.species,
+         text = result
+      }
    end
 }

=== modified file 'data/tribes/scripting/help/building_help.lua'
--- data/tribes/scripting/help/building_help.lua	2016-03-02 17:13:06 +0000
+++ data/tribes/scripting/help/building_help.lua	2016-03-22 12:21:57 +0000
@@ -346,10 +346,9 @@
       local soldier = wl.Game():get_worker_description(tribe.soldier)
       for j, buildcost in ipairs(soldier.buildcost) do
          if (buildcost == ware) then
-            for k, buildingname in ipairs(tribe.buildings) do
-               local warehouse_description = wl.Game():get_building_description(buildingname)
-               if (warehouse_description.type_name == "warehouse") then
-                  outgoing = outgoing .. dependencies({ware, warehouse_description, soldier}, soldier.descname)
+            for k, building in ipairs(tribe.buildings) do
+               if (building.type_name == "warehouse") then
+                  outgoing = outgoing .. dependencies({ware, building, soldier}, soldier.descname)
                end
             end
          end
@@ -760,12 +759,15 @@
 
 -- The main function call
 return {
-   func = function(tribename, building)
+   func = function(tribename, buildingname)
       set_textdomain("tribes_encyclopedia")
       local tribe = wl.Game():get_tribe_description(tribename)
       -- We need to get the building description again, because it will
       -- give us a cast to the appropriate subclass.
-      local building_description = wl.Game():get_building_description(building.name)
-      return building_help(tribe, building_description)
+      local building_description = wl.Game():get_building_description(buildingname)
+      return {
+         title = building_description.descname,
+         text = building_help(tribe, building_description)
+      }
    end
 }

=== added file 'data/tribes/scripting/help/init.lua'
--- data/tribes/scripting/help/init.lua	1970-01-01 00:00:00 +0000
+++ data/tribes/scripting/help/init.lua	2016-03-22 12:21:57 +0000
@@ -0,0 +1,84 @@
+-- Returns definitions for encyclopedia tabs and their contents for the
+-- Tribal Encyclopedia
+
+-- Comparison function used to sort map objects alphabetically
+function compare_by_title(a, b)
+  return a["title"] < b["title"]
+end
+
+-- Helper function to return all entries of a certain type for the tribe
+function map_object_entries(tribename, script_filename, map_object_table)
+   local result = {}
+   for i, map_object in ipairs(map_object_table) do
+      result[i] = {
+         name = map_object.name,
+         title = map_object.descname,
+         icon = map_object.icon_name,
+         script = script_filename,
+         script_parameters = {[1] = tribename, [2] = map_object.name}
+      }
+   end
+   table.sort(result, compare_by_title)
+   return result
+end
+
+-- Returns help entries for all the buildings that the tribe has
+function building_entries(tribename)
+   local tribe = wl.Game():get_tribe_description(tribename)
+   return map_object_entries(tribename, "tribes/scripting/help/building_help.lua", tribe.buildings)
+end
+
+-- Returns help entries for all the wares that the tribe can use
+function ware_entries(tribename)
+   local tribe = wl.Game():get_tribe_description(tribename)
+   return map_object_entries(tribename, "tribes/scripting/help/ware_help.lua", tribe.wares)
+end
+
+-- Returns help entries for all the workers that the tribe can use
+function worker_entries(tribename)
+   local tribe = wl.Game():get_tribe_description(tribename)
+   return map_object_entries(tribename, "tribes/scripting/help/worker_help.lua", tribe.workers)
+end
+
+-- Main function
+return {
+   func = function(tribename)
+      set_textdomain("tribes_encyclopedia")
+      return {
+         title = _"Tribal Encyclopedia",
+         tabs = {
+            {
+               name = "general",
+               title = _"General",
+               icon = "images/logos/wl-ico-32.png",
+               entries = {
+                  {
+                     name = "intro",
+                     title = _"Introduction",
+                     script = "txts/README.lua",
+                     script_parameters = {}
+                  }
+               }
+            },
+            {
+               name = "wares",
+               title = _"Wares",
+               icon = "images/wui/buildings/menu_tab_wares.png",
+               entries = ware_entries(tribename)
+            },
+            {
+               name = "workers",
+               title = _"Workers",
+               icon = "images/wui/buildings/menu_tab_workers.png",
+               entries = worker_entries(tribename)
+            },
+            {
+               name = "buildings",
+               title = _"Buildings",
+               icon = "images/wui/stats/genstats_nrbuildings.png",
+               entries = building_entries(tribename)
+            },
+         }
+      }
+   end
+}

=== modified file 'data/tribes/scripting/help/ware_help.lua'
--- data/tribes/scripting/help/ware_help.lua	2015-11-05 12:53:49 +0000
+++ data/tribes/scripting/help/ware_help.lua	2016-03-22 12:21:57 +0000
@@ -134,8 +134,7 @@
 
    -- Now collecting the workers that use this ware as a tool
    local workers_string = ""
-   for i, workername in ipairs(tribe.workers) do
-   local worker = wl.Game():get_worker_description(workername)
+   for i, worker in ipairs(tribe.workers) do
       local add_this_worker = false
       for j, buildcost in ipairs(worker.buildcost) do
          if (buildcost ~= nil and buildcost == ware_description.name) then
@@ -165,12 +164,16 @@
 
 
 return {
-   func = function(tribename, ware_description)
+   func = function(tribename, warename)
       set_textdomain("tribes_encyclopedia")
       local tribe = wl.Game():get_tribe_description(tribename)
+      local ware_description = wl.Game():get_ware_description(warename)
       include(ware_description.helptext_script)
-      return ware_help_general_string(tribe, ware_description)
-         .. ware_help_producers_string(tribe, ware_description)
-         .. ware_help_consumers_string(tribe, ware_description)
+      return {
+         title = ware_description.descname,
+         text = ware_help_general_string(tribe, ware_description)
+            .. ware_help_producers_string(tribe, ware_description)
+            .. ware_help_consumers_string(tribe, ware_description)
+      }
    end
 }

=== modified file 'data/tribes/scripting/help/worker_help.lua'
--- data/tribes/scripting/help/worker_help.lua	2016-02-27 08:43:39 +0000
+++ data/tribes/scripting/help/worker_help.lua	2016-03-22 12:21:57 +0000
@@ -12,8 +12,7 @@
 
 function worker_help_producers_string(tribe, worker_description)
    local result = ""
-   for i, building_name in ipairs(tribe.buildings) do
-      local building = wl.Game():get_building_description(building_name)
+   for i, building in ipairs(tribe.buildings) do
       if (building.type_name == "productionsite") then
          local recruits_this = false;
          for j, output in ipairs(building.output_worker_types) do
@@ -165,9 +164,13 @@
 
 
 return {
-   func = function(tribename, worker_description)
+   func = function(tribename, workername)
       set_textdomain("tribes_encyclopedia")
       local tribe = wl.Game():get_tribe_description(tribename)
-      return worker_help_string(tribe, worker_description)
+      local worker_description = wl.Game():get_worker_description(workername)
+      return {
+         title = worker_description.descname,
+         text = worker_help_string(tribe, worker_description)
+      }
    end
 }

=== modified file 'data/tribes/scripting/starting_conditions/atlanteans/trading_outpost.lua'
--- data/tribes/scripting/starting_conditions/atlanteans/trading_outpost.lua	2016-03-08 07:40:58 +0000
+++ data/tribes/scripting/starting_conditions/atlanteans/trading_outpost.lua	2016-03-22 12:21:57 +0000
@@ -108,8 +108,8 @@
       -- Get all warehouse types
       local plr = wl.Game().players[player.number]
       local warehouse_types = {}
-      for i, building_name in ipairs(wl.Game():get_tribe_description(plr.tribe_name).buildings) do
-         if (wl.Game():get_building_description(building_name).type_name == "warehouse") then
+      for i, building in ipairs(wl.Game():get_tribe_description(plr.tribe_name).buildings) do
+         if (building.type_name == "warehouse") then
             table.insert(warehouse_types, building_name)
          end
       end

=== modified file 'data/tribes/scripting/starting_conditions/barbarians/trading_outpost.lua'
--- data/tribes/scripting/starting_conditions/barbarians/trading_outpost.lua	2016-03-08 07:40:58 +0000
+++ data/tribes/scripting/starting_conditions/barbarians/trading_outpost.lua	2016-03-22 12:21:57 +0000
@@ -99,8 +99,8 @@
       -- Get all warehouse types
       local plr = wl.Game().players[player.number]
       local warehouse_types = {}
-      for i, building_name in ipairs(wl.Game():get_tribe_description(plr.tribe_name).buildings) do
-         if (wl.Game():get_building_description(building_name).type_name == "warehouse") then
+      for i, building in ipairs(wl.Game():get_tribe_description(plr.tribe_name).buildings) do
+         if (building.type_name == "warehouse") then
             table.insert(warehouse_types, building_name)
          end
       end

=== modified file 'data/tribes/scripting/starting_conditions/empire/trading_outpost.lua'
--- data/tribes/scripting/starting_conditions/empire/trading_outpost.lua	2016-03-08 07:40:58 +0000
+++ data/tribes/scripting/starting_conditions/empire/trading_outpost.lua	2016-03-22 12:21:57 +0000
@@ -105,8 +105,8 @@
       -- Get all warehouse types
       local plr = wl.Game().players[player.number]
       local warehouse_types = {}
-      for i, building_name in ipairs(wl.Game():get_tribe_description(plr.tribe_name).buildings) do
-         if (wl.Game():get_building_description(building_name).type_name == "warehouse") then
+      for i, building in ipairs(wl.Game():get_tribe_description(plr.tribe_name).buildings) do
+         if (building.type_name == "warehouse") then
             table.insert(warehouse_types, building_name)
          end
       end

=== modified file 'src/editor/CMakeLists.txt'
--- src/editor/CMakeLists.txt	2016-01-24 11:11:54 +0000
+++ src/editor/CMakeLists.txt	2016-03-22 12:21:57 +0000
@@ -103,7 +103,6 @@
     scripting_lua_interface
     scripting_lua_table
     ui_basic
-    ui_fsmenu
     widelands_ball_of_mud
     wui
     wui_common

=== modified file 'src/editor/editorinteractive.cc'
--- src/editor/editorinteractive.cc	2016-03-19 11:47:00 +0000
+++ src/editor/editorinteractive.cc	2016-03-22 12:21:57 +0000
@@ -370,7 +370,7 @@
 	if (helpmenu_.window)
 		delete helpmenu_.window;
 	else
-		new EditorHelp(*this, helpmenu_);
+		new EditorHelp(*this, helpmenu_, &egbase().lua());
 }
 
 

=== modified file 'src/editor/ui_menus/editor_help.cc'
--- src/editor/ui_menus/editor_help.cc	2016-02-13 11:30:01 +0000
+++ src/editor/ui_menus/editor_help.cc	2016-03-22 12:21:57 +0000
@@ -19,206 +19,30 @@
 
 #include "editor/ui_menus/editor_help.h"
 
-#include <algorithm>
-#include <map>
+
 #include <memory>
-#include <string>
-#include <vector>
 
 #include <boost/format.hpp>
 
 #include "base/i18n.h"
 #include "editor/editorinteractive.h"
-#include "graphic/graphic.h"
-#include "graphic/texture.h"
-#include "io/filesystem/layered_filesystem.h"
-#include "logic/map_objects/world/editor_category.h"
-#include "logic/map_objects/world/terrain_description.h"
-#include "logic/map_objects/world/world.h"
+#include "scripting/lua_coroutine.h"
 #include "scripting/lua_interface.h"
 #include "scripting/lua_table.h"
-
-namespace {
-
-#define WINDOW_WIDTH std::min(700, g_gr->get_xres() - 40)
-#define WINDOW_HEIGHT std::min(550, g_gr->get_yres() - 40)
-
-constexpr int kPadding = 5;
-constexpr int kTabHeight = 35;
-
-const std::string heading(const std::string& text) {
-	return ((boost::format("<rt><p font-size=18 font-weight=bold font-color=D1D1D1>"
-	                       "%s<br></p><p font-size=8> <br></p></rt>") %
-	         text).str());
-}
-
-}  // namespace
-
-inline EditorInteractive& EditorHelp::eia() const {
-	return dynamic_cast<EditorInteractive&>(*get_parent());
-}
-
-EditorHelp::EditorHelp(EditorInteractive& parent, UI::UniqueWindow::Registry& registry)
-   : UI::UniqueWindow(&parent, "encyclopedia", &registry, WINDOW_WIDTH, WINDOW_HEIGHT, _("Help")),
-     tabs_(this, 0, 0, nullptr) {
-
-	const int contents_height = WINDOW_HEIGHT - kTabHeight - 2 * kPadding;
-	const int contents_width = WINDOW_WIDTH / 2 - 1.5 * kPadding;
-
-	std::vector<std::unique_ptr<HelpTab>> tab_definitions;
-
-	tab_definitions.push_back(
-	   std::unique_ptr<HelpTab>(new HelpTab("terrains",
-	                                        "images/wui/editor/editor_menu_tool_set_terrain.png",
-	                                        _("Terrains"),
-	                                        "scripting/editor/terrain_help.lua",
-	                                        HelpEntry::Type::kTerrain)));
-
-	tab_definitions.push_back(
-	   std::unique_ptr<HelpTab>(new HelpTab("trees",
-	                                        "world/immovables/trees/alder/old/idle_0.png",
-	                                        _("Trees"),
-	                                        "scripting/editor/tree_help.lua",
-	                                        HelpEntry::Type::kTree)));
-
-	for (const auto& tab : tab_definitions) {
-		// Make sure that all paths exist
-		if (!g_fs->file_exists(tab->script_path)) {
-			throw wexception("Script path %s for tab %s does not exist!",
-			                 tab->script_path.c_str(),
-			                 tab->key.c_str());
-		}
-		if (!g_fs->file_exists(tab->image_filename)) {
-			throw wexception("Image path %s for tab %s does not exist!",
-			                 tab->image_filename.c_str(),
-			                 tab->key.c_str());
-		}
-
-		wrapper_boxes_.insert(std::make_pair(
-		   tab->key, std::unique_ptr<UI::Box>(new UI::Box(&tabs_, 0, 0, UI::Box::Horizontal))));
-
-		boxes_.insert(
-		   std::make_pair(tab->key,
-		                  std::unique_ptr<UI::Box>(new UI::Box(
-		                     wrapper_boxes_.at(tab->key).get(), 0, 0, UI::Box::Horizontal))));
-
-		lists_.insert(
-		   std::make_pair(tab->key,
-		                  std::unique_ptr<UI::Listselect<Widelands::DescriptionIndex>>(
-		                     new UI::Listselect<Widelands::DescriptionIndex>(
-		                        boxes_.at(tab->key).get(), 0, 0, contents_width, contents_height))));
-		lists_.at(tab->key)->selected.connect(
-		   boost::bind(&EditorHelp::entry_selected, this, tab->key, tab->script_path, tab->type));
-
-		contents_.insert(
-		   std::make_pair(tab->key,
-		                  std::unique_ptr<UI::MultilineTextarea>(new UI::MultilineTextarea(
-		                     boxes_.at(tab->key).get(), 0, 0, contents_width, contents_height))));
-
-		boxes_.at(tab->key)->add(lists_.at(tab->key).get(), UI::Align::kLeft);
-		boxes_.at(tab->key)->add_space(kPadding);
-		boxes_.at(tab->key)->add(contents_.at(tab->key).get(), UI::Align::kLeft);
-
-		wrapper_boxes_.at(tab->key)->add_space(kPadding);
-		wrapper_boxes_.at(tab->key)->add(boxes_.at(tab->key).get(), UI::Align::kLeft);
-
-		tabs_.add("editor_help_" + tab->key,
-		          g_gr->images().get(tab->image_filename),
-		          wrapper_boxes_.at(tab->key).get(),
-		          tab->tooltip);
-	}
-	tabs_.set_size(WINDOW_WIDTH, WINDOW_HEIGHT);
-
-	fill_terrains();
-	fill_trees();
-
-	if (get_usedefaultpos()) {
-		center_to_parent();
-	}
-}
-
-void EditorHelp::fill_entries(const char* key, std::vector<HelpEntry>* entries) {
-	std::sort(entries->begin(), entries->end());
-	for (uint32_t i = 0; i < entries->size(); i++) {
-		HelpEntry cur = (*entries)[i];
-		lists_.at(key)->add(cur.descname, cur.index, cur.icon);
-	}
-	lists_.at(key)->select(0);
-}
-
-void EditorHelp::fill_terrains() {
-	const Widelands::World& world = eia().egbase().world();
-	std::vector<HelpEntry> entries;
-
-	for (Widelands::DescriptionIndex i = 0; i < world.terrains().size(); ++i) {
-		const Widelands::TerrainDescription& terrain = world.terrain_descr(i);
-		upcast(Image const, icon, &terrain.get_texture(0));
-		/** TRANSLATORS: Terrain name + editor category, e.g. Steppe (Summer) */
-		HelpEntry entry(i, (boost::format(_("%1% (%2%)"))
-								  % terrain.descname().c_str()
-								  % terrain.editor_category()->descname()).str(), icon);
-		entries.push_back(entry);
-	}
-	fill_entries("terrains", &entries);
-}
-
-void EditorHelp::fill_trees() {
-	const Widelands::World& world = eia().egbase().world();
-	std::vector<HelpEntry> entries;
-
-	for (Widelands::DescriptionIndex i = 0; i < world.get_nr_immovables(); ++i) {
-		const Widelands::ImmovableDescr* immovable = world.get_immovable_descr(i);
-		uint32_t attribute_id = immovable->get_attribute_id("tree");
-		if (immovable->has_attribute(attribute_id)) {
-			const Image* icon = immovable->representative_image();
-			HelpEntry entry(i, immovable->species(), icon);
-			entries.push_back(entry);
-		}
-	}
-	fill_entries("trees", &entries);
-}
-
-void EditorHelp::entry_selected(const std::string& key,
-                                const std::string& script_path,
-                                const HelpEntry::Type& type) {
+#include "ui_basic/messagebox.h"
+
+EditorHelp::EditorHelp(EditorInteractive& parent, UI::UniqueWindow::Registry& registry, LuaInterface* const lua)
+	: EncyclopediaWindow(parent, registry, lua)
+{
 	try {
-		std::unique_ptr<LuaTable> table(eia().egbase().lua().run_script(script_path));
-		std::unique_ptr<LuaCoroutine> cr(table->get_coroutine("func"));
-
-		std::string descname = "";
-
-		switch (type) {
-		case (HelpEntry::Type::kTerrain): {
-			const Widelands::TerrainDescription& descr =
-			   eia().egbase().world().terrain_descr(lists_.at(key)->get_selected());
-			/** TRANSLATORS: Terrain name + editor category, e.g. Steppe (Summer) */
-			descname = (boost::format(_("%1% (%2%)"))
-						  % descr.descname().c_str()
-						  % descr.editor_category()->descname()).str();
-			cr->push_arg(descr.name());
-			break;
-		}
-		case (HelpEntry::Type::kTree): {
-			const Widelands::ImmovableDescr* descr =
-			   eia().egbase().world().get_immovable_descr(lists_.at(key)->get_selected());
-			descname = descr->species();
-			cr->push_arg(descr->name());
-			break;
-		}
-		default:
-			throw wexception("EditorHelp: No Type defined for tab.");
-		}
-
-		cr->resume();
-		const std::string help_text = cr->pop_string();
-
-		// We add the heading here instead of in Lua, so that the content can be
-		// reused in a standalone window that will have the heading as a window
-		// title.
-		contents_.at(key)->set_text((boost::format("%s%s") % heading(descname) % help_text).str());
-
+		init(parent, lua_->run_script("scripting/editor/editor_help.lua"));
 	} catch (LuaError& err) {
-		contents_.at(key)->set_text(err.what());
+		log("Error loading script for editor help:\n%s\n", err.what());
+		UI::WLMessageBox wmb(
+					&parent,
+					_("Error!"),
+					(boost::format("Error loading script for editor help:\n%s") % err.what()).str(),
+					UI::WLMessageBox::MBoxType::kOk);
+		wmb.run<UI::Panel::Returncodes>();
 	}
-	contents_.at(key)->scroll_to_top();
 }

=== modified file 'src/editor/ui_menus/editor_help.h'
--- src/editor/ui_menus/editor_help.h	2016-03-07 19:06:19 +0000
+++ src/editor/ui_menus/editor_help.h	2016-03-22 12:21:57 +0000
@@ -20,87 +20,14 @@
 #ifndef WL_EDITOR_UI_MENUS_EDITOR_HELP_H
 #define WL_EDITOR_UI_MENUS_EDITOR_HELP_H
 
-#include <map>
-#include <memory>
-#include <vector>
-
-#include "logic/map_objects/map_object.h"
-#include "ui_basic/box.h"
-#include "ui_basic/listselect.h"
-#include "ui_basic/multilinetextarea.h"
-#include "ui_basic/table.h"
-#include "ui_basic/tabpanel.h"
+#include "scripting/lua_interface.h"
+#include "ui_basic/encyclopedia_window.h"
 #include "ui_basic/unique_window.h"
-#include "ui_basic/window.h"
 
 class EditorInteractive;
 
-struct EditorHelp : public UI::UniqueWindow {
-	EditorHelp(EditorInteractive&, UI::UniqueWindow::Registry&);
-
-private:
-	struct HelpEntry {
-		enum class Type {
-			kTerrain,
-			kTree
-		};
-
-		HelpEntry(const Widelands::DescriptionIndex init_index,
-					 const std::string& init_descname,
-					 const Image* init_icon)
-			: index(init_index), descname(init_descname), icon(init_icon) {
-		}
-		Widelands::DescriptionIndex index;
-		std::string descname;
-		const Image* icon;
-
-		bool operator<(const HelpEntry& other) const {
-			return descname < other.descname;
-		}
-	};
-
-	struct HelpTab {
-		HelpTab(const std::string& _key,
-		        const std::string& _image_filename,
-		        const std::string& _tooltip,
-		        const std::string& _script_path,
-		        const EditorHelp::HelpEntry::Type _type)
-		   : key(_key),
-		     image_filename(_image_filename),
-		     tooltip(_tooltip),
-		     script_path(_script_path),
-		     type(_type) {
-		}
-		const std::string key;
-		const std::string image_filename;
-		const std::string tooltip;
-		const std::string script_path;
-		const EditorHelp::HelpEntry::Type type;
-	};
-
-	EditorInteractive& eia() const;
-
-	// Fill table of contents
-	void fill_entries(const char* key, std::vector<HelpEntry>* entries);
-	void fill_terrains();
-	void fill_trees();
-
-	// Update contents when an entry is selected
-	void entry_selected(const std::string& key,
-	                    const std::string& script_path,
-	                    const HelpEntry::Type& type);
-
-	// UI elements
-	UI::TabPanel tabs_;
-
-	// Wrapper boxes so we can add some padding
-	std::map<std::string, std::unique_ptr<UI::Box>> wrapper_boxes_;
-	// Main contents boxes for each tab
-	std::map<std::string, std::unique_ptr<UI::Box>> boxes_;
-	// A tab's table of contents
-	std::map<std::string, std::unique_ptr<UI::Listselect<Widelands::DescriptionIndex>>> lists_;
-	// The contents shown when an entry is selected in a tab
-	std::map<std::string, std::unique_ptr<UI::MultilineTextarea>> contents_;
+struct EditorHelp : public UI::EncyclopediaWindow {
+	EditorHelp(EditorInteractive&, UI::UniqueWindow::Registry&, LuaInterface* const lua);
 };
 
 #endif  // end of include guard: WL_EDITOR_UI_MENUS_EDITOR_HELP_H

=== modified file 'src/editor/ui_menus/editor_main_menu.cc'
--- src/editor/ui_menus/editor_main_menu.cc	2016-02-18 18:27:52 +0000
+++ src/editor/ui_menus/editor_main_menu.cc	2016-03-22 12:21:57 +0000
@@ -26,7 +26,6 @@
 #include "editor/ui_menus/editor_main_menu_new_map.h"
 #include "editor/ui_menus/editor_main_menu_random_map.h"
 #include "editor/ui_menus/editor_main_menu_save_map.h"
-#include "ui_fsmenu/fileview.h"
 
 // TODO(unknown): these should be defined globally for the whole UI
 #define width 200
@@ -71,11 +70,6 @@
 		 0, 0, width, 0,
 		 g_gr->images().get("images/ui_basic/but1.png"),
 		 _("Map Options")),
-	button_view_readme_
-		(&box_, "readme",
-		 0, 0, width, 0,
-		 g_gr->images().get("images/ui_basic/but1.png"),
-		 _("View Readme")),
 	button_exit_editor_
 		(&box_, "exit",
 		 0, 0, width, 0,
@@ -87,9 +81,8 @@
 	box_.add(&button_load_map_, UI::Align::kHCenter);
 	box_.add(&button_save_map_, UI::Align::kHCenter);
 	box_.add(&button_map_options_, UI::Align::kHCenter);
-	box_.add(&button_view_readme_, UI::Align::kHCenter);
 	box_.add(&button_exit_editor_, UI::Align::kHCenter);
-	box_.set_size(width, 7 * button_new_map_.get_h()+ 6 * vspacing);
+	box_.set_size(width, 6 * button_new_map_.get_h()+ 5 * vspacing);
 	set_inner_size(get_inner_w(), box_.get_h() + 2 * margin);
 
 	button_new_map_.sigclicked.connect(boost::bind(&EditorMainMenu::new_map_btn, this));
@@ -97,13 +90,6 @@
 	button_load_map_.sigclicked.connect(boost::bind(&EditorMainMenu::load_btn, this));
 	button_save_map_.sigclicked.connect(boost::bind(&EditorMainMenu::save_btn, this));
 	button_map_options_.sigclicked.connect(boost::bind(&EditorMainMenu::map_options_btn, this));
-
-	window_readme_.open_window = [this] {
-		fileview_window(eia(), window_readme_, "txts/editor_readme.lua");
-	};
-	button_view_readme_.sigclicked.connect(
-		boost::bind(&UI::UniqueWindow::Registry::toggle, window_readme_));
-
 	button_exit_editor_.sigclicked.connect(boost::bind(&EditorMainMenu::exit_btn, this));
 
 	// Put in the default position, if necessary

=== modified file 'src/editor/ui_menus/editor_main_menu.h'
--- src/editor/ui_menus/editor_main_menu.h	2016-01-16 12:55:14 +0000
+++ src/editor/ui_menus/editor_main_menu.h	2016-03-22 12:21:57 +0000
@@ -40,11 +40,8 @@
 	UI::Button button_load_map_;
 	UI::Button button_save_map_;
 	UI::Button button_map_options_;
-	UI::Button button_view_readme_;
 	UI::Button button_exit_editor_;
 
-	UI::UniqueWindow::Registry window_readme_;
-
 	void exit_btn       ();
 	void load_btn       ();
 	void save_btn       ();

=== modified file 'src/scripting/lua_coroutine.cc'
--- src/scripting/lua_coroutine.cc	2016-02-12 16:58:41 +0000
+++ src/scripting/lua_coroutine.cc	2016-03-22 12:21:57 +0000
@@ -19,6 +19,8 @@
 
 #include "scripting/lua_coroutine.h"
 
+#include <memory>
+
 #include "io/fileread.h"
 #include "io/filewrite.h"
 #include "scripting/lua_errors.h"
@@ -99,24 +101,6 @@
 	++ninput_args_;
 }
 
-void LuaCoroutine::push_arg(const Widelands::BuildingDescr* building_descr) {
-	assert(building_descr != nullptr);
-	to_lua<LuaMaps::LuaBuildingDescription>(lua_state_, new LuaMaps::LuaBuildingDescription(building_descr));
-	++ninput_args_;
-}
-
-void LuaCoroutine::push_arg(const Widelands::WareDescr* ware_descr) {
-	assert(ware_descr != nullptr);
-	to_lua<LuaMaps::LuaWareDescription>(lua_state_, new LuaMaps::LuaWareDescription(ware_descr));
-	++ninput_args_;
-}
-
-void LuaCoroutine::push_arg(const Widelands::WorkerDescr* worker_descr) {
-	assert(worker_descr != nullptr);
-	to_lua<LuaMaps::LuaWorkerDescription>(lua_state_, new LuaMaps::LuaWorkerDescription(worker_descr));
-	++ninput_args_;
-}
-
 void LuaCoroutine::push_arg(const std::string& string) {
 	assert(!string.empty());
 	lua_pushstring(lua_state_, string);
@@ -149,6 +133,17 @@
 	return return_value;
 }
 
+std::unique_ptr<LuaTable> LuaCoroutine::pop_table() {
+	std::unique_ptr<LuaTable> result(nullptr);
+	if (!nreturn_values_) {
+		return result;
+	}
+	result.reset(new LuaTable(lua_state_));
+	lua_pop(lua_state_, 1);
+	--nreturn_values_;
+	return result;
+}
+
 constexpr uint8_t kCoroutineDataPacketVersion = 3;
 void LuaCoroutine::write(FileWrite& fw) {
 	fw.unsigned_8(kCoroutineDataPacketVersion);

=== modified file 'src/scripting/lua_coroutine.h'
--- src/scripting/lua_coroutine.h	2016-02-12 16:58:41 +0000
+++ src/scripting/lua_coroutine.h	2016-03-22 12:21:57 +0000
@@ -20,6 +20,7 @@
 #ifndef WL_SCRIPTING_LUA_COROUTINE_H
 #define WL_SCRIPTING_LUA_COROUTINE_H
 
+#include <memory>
 #include <string>
 
 #include <stdint.h>
@@ -28,6 +29,7 @@
 
 class FileRead;
 class FileWrite;
+class LuaTable;
 
 namespace Widelands {
 class Player;
@@ -62,14 +64,12 @@
 	// in hooks.
 	void push_arg(const Widelands::Player*);
 	void push_arg(const Widelands::Coords&);
-	void push_arg(const Widelands::BuildingDescr*);
-	void push_arg(const Widelands::WareDescr*);
-	void push_arg(const Widelands::WorkerDescr*);
 	void push_arg(const std::string&);
 
 	// Accesses the returned values from the run of the coroutine.
 	uint32_t pop_uint32();
 	std::string pop_string();
+	std::unique_ptr<LuaTable> pop_table();
 
 private:
 	friend class LuaGameInterface;

=== modified file 'src/scripting/lua_map.cc'
--- src/scripting/lua_map.cc	2016-03-19 12:51:22 +0000
+++ src/scripting/lua_map.cc	2016-03-22 12:21:57 +0000
@@ -1108,15 +1108,16 @@
 /* RST
 	.. attribute:: buildings
 
-			(RO) an array of :class:`string` with the names of all the buildings that the tribe can use
+			(RO) an array of :class:`LuaBuildingDescription` with all the buildings that the tribe can use,
+				  casted to their appropriate subclasses.
 */
-
 int LuaTribeDescription::get_buildings(lua_State * L) {
+	const TribeDescr& tribe = *get();
 	lua_newtable(L);
 	int counter = 0;
-	for (DescriptionIndex building : get()->buildings()) {
+	for (DescriptionIndex building : tribe.buildings()) {
 		lua_pushinteger(L, ++counter);
-		lua_pushstring(L, get_egbase(L).tribes().get_building_descr(building)->name());
+		upcasted_map_object_descr_to_lua(L, tribe.get_building_descr(building));
 		lua_settable(L, -3);
 	}
 	return 1;
@@ -1230,15 +1231,15 @@
 /* RST
 	.. attribute:: wares
 
-			(RO) an array of :class:`string` with the names of all the wares that the tribe uses
+			(RO) an array of :class:`LuaWareDescription` with all the wares that the tribe can use.
 */
-
 int LuaTribeDescription::get_wares(lua_State * L) {
+	const TribeDescr& tribe = *get();
 	lua_newtable(L);
 	int counter = 0;
-	for (DescriptionIndex ware : get()->wares()) {
+	for (DescriptionIndex ware : tribe.wares()) {
 		lua_pushinteger(L, ++counter);
-		lua_pushstring(L, get_egbase(L).tribes().get_ware_descr(ware)->name());
+		to_lua<LuaWareDescription>(L, new LuaWareDescription(tribe.get_ware_descr(ware)));
 		lua_settable(L, -3);
 	}
 	return 1;
@@ -1247,15 +1248,16 @@
 /* RST
 	.. attribute:: workers
 
-			(RO) an array of :class:`string` with the names of all the workers that the tribe can use
+			(RO) an array of :class:`LuaWorkerDescription` with all the workers that the tribe can use,
+				  casted to their appropriate subclasses.
 */
-
 int LuaTribeDescription::get_workers(lua_State * L) {
+	const TribeDescr& tribe = *get();
 	lua_newtable(L);
 	int counter = 0;
-	for (DescriptionIndex worker : get()->workers()) {
+	for (DescriptionIndex worker : tribe.workers()) {
 		lua_pushinteger(L, ++counter);
-		lua_pushstring(L, get_egbase(L).tribes().get_worker_descr(worker)->name());
+		upcasted_map_object_descr_to_lua(L, tribe.get_worker_descr(worker));
 		lua_settable(L, -3);
 	}
 	return 1;

=== modified file 'src/ui_basic/CMakeLists.txt'
--- src/ui_basic/CMakeLists.txt	2015-11-28 11:45:40 +0000
+++ src/ui_basic/CMakeLists.txt	2016-03-22 12:21:57 +0000
@@ -8,6 +8,8 @@
     checkbox.h
     editbox.cc
     editbox.h
+    encyclopedia_window.cc
+    encyclopedia_window.h
     icon.cc
     icon.h
     icongrid.cc
@@ -60,6 +62,9 @@
     graphic_text_layout
     io_filesystem
     profile
+    scripting_coroutine
+    scripting_lua_interface
+    scripting_lua_table
     sound
     widelands_ball_of_mud
 )

=== renamed file 'src/wui/encyclopedia_window.cc' => 'src/ui_basic/encyclopedia_window.cc'
--- src/wui/encyclopedia_window.cc	2016-02-14 14:09:29 +0000
+++ src/ui_basic/encyclopedia_window.cc	2016-03-22 12:21:57 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2002-2004, 2006-2011 by the Widelands Development Team
+ * Copyright (C) 2002-2016 by the Widelands Development Team
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -17,14 +17,10 @@
  *
  */
 
-#include "wui/encyclopedia_window.h"
+#include "ui_basic/encyclopedia_window.h"
 
-#include <algorithm>
-#include <cstring>
 #include <map>
 #include <memory>
-#include <set>
-#include <string>
 #include <vector>
 
 #include <boost/format.hpp>
@@ -32,15 +28,10 @@
 #include "base/i18n.h"
 #include "graphic/graphic.h"
 #include "io/filesystem/layered_filesystem.h"
-#include "logic/map_objects/tribes/building.h"
 #include "logic/map_objects/tribes/tribe_descr.h"
-#include "logic/map_objects/tribes/tribes.h"
-#include "logic/map_objects/tribes/ware_descr.h"
-#include "logic/map_objects/tribes/worker_descr.h"
-#include "logic/player.h"
-#include "scripting/lua_interface.h"
-#include "scripting/lua_table.h"
-#include "wui/interactive_player.h"
+#include "scripting/lua_coroutine.h"
+#include "ui_basic/messagebox.h"
+#include "wui/interactive_base.h"
 
 namespace {
 
@@ -50,25 +41,6 @@
 constexpr int kPadding = 5;
 constexpr int kTabHeight = 35;
 
-struct EncyclopediaTab {
-	EncyclopediaTab(const std::string& init_key,
-						 const std::string& init_image_filename,
-						 const std::string& init_tooltip,
-						 const std::string& init_script_path,
-						 const Widelands::MapObjectType init_type)
-		: key(init_key),
-		  image_filename(init_image_filename),
-		  tooltip(init_tooltip),
-		  script_path(init_script_path),
-		  type(init_type) {
-	}
-	const std::string key;
-	const std::string image_filename;
-	const std::string tooltip;
-	const std::string script_path;
-	const Widelands::MapObjectType type;
-};
-
 const std::string heading(const std::string& text) {
 	return ((boost::format("<rt><p font-size=18 font-weight=bold font-color=D1D1D1>"
 	                       "%s<br></p><p font-size=8> <br></p></rt>") %
@@ -77,204 +49,142 @@
 
 }  // namespace
 
-inline InteractivePlayer& EncyclopediaWindow::iaplayer() const {
-	return dynamic_cast<InteractivePlayer&>(*get_parent());
-}
+namespace UI {
 
-EncyclopediaWindow::EncyclopediaWindow(InteractivePlayer& parent, UI::UniqueWindow::Registry& registry)
+EncyclopediaWindow::EncyclopediaWindow(InteractiveBase& parent, UI::UniqueWindow::Registry& registry, LuaInterface* const lua)
 	: UI::UniqueWindow(
-        &parent, "encyclopedia", &registry, WINDOW_WIDTH, WINDOW_HEIGHT, _("Tribal Encyclopedia")),
-     tabs_(this, 0, 0, nullptr) {
+		  &parent, "encyclopedia", &registry, WINDOW_WIDTH, WINDOW_HEIGHT, ""),
+	  lua_(lua),
+	  tabs_(this, 0, 0, nullptr)
+		{}
+
+void EncyclopediaWindow::init(InteractiveBase& parent, std::unique_ptr<LuaTable> table) {
 
 	const int contents_height = WINDOW_HEIGHT - kTabHeight - 2 * kPadding;
 	const int contents_width = WINDOW_WIDTH / 2 - 1.5 * kPadding;
 
-	std::vector<std::unique_ptr<EncyclopediaTab>> tab_definitions;
-
-	tab_definitions.push_back(
-	   std::unique_ptr<EncyclopediaTab>(new EncyclopediaTab("wares",
-																			  "images/wui/buildings/menu_tab_wares.png",
-	                                                        _("Wares"),
-	                                                        "tribes/scripting/help/ware_help.lua",
-	                                                        Widelands::MapObjectType::WARE)));
-
-	tab_definitions.push_back(
-	   std::unique_ptr<EncyclopediaTab>(new EncyclopediaTab("workers",
-																			  "images/wui/buildings/menu_tab_workers.png",
-	                                                        _("Workers"),
-	                                                        "tribes/scripting/help/worker_help.lua",
-	                                                        Widelands::MapObjectType::WORKER)));
-
-	tab_definitions.push_back(std::unique_ptr<EncyclopediaTab>(
-	   new EncyclopediaTab("buildings",
-								  "images/wui/stats/genstats_nrbuildings.png",
-	                       _("Buildings"),
-	                       "tribes/scripting/help/building_help.lua",
-	                       Widelands::MapObjectType::BUILDING)));
-
-	for (const auto& tab : tab_definitions) {
-		// Make sure that all paths exist
-		if (!g_fs->file_exists(tab->script_path)) {
-			throw wexception("Script path %s for tab %s does not exist!",
-			                 tab->script_path.c_str(),
-			                 tab->key.c_str());
-		}
-		if (!g_fs->file_exists(tab->image_filename)) {
-			throw wexception("Image path %s for tab %s does not exist!",
-			                 tab->image_filename.c_str(),
-			                 tab->key.c_str());
-		}
-
-		wrapper_boxes_.insert(std::make_pair(
-		   tab->key, std::unique_ptr<UI::Box>(new UI::Box(&tabs_, 0, 0, UI::Box::Horizontal))));
-
-		boxes_.insert(
-		   std::make_pair(tab->key,
-		                  std::unique_ptr<UI::Box>(new UI::Box(
-		                     wrapper_boxes_.at(tab->key).get(), 0, 0, UI::Box::Horizontal))));
-
-		lists_.insert(
-		   std::make_pair(tab->key,
-		                  std::unique_ptr<UI::Listselect<Widelands::DescriptionIndex>>(
-		                     new UI::Listselect<Widelands::DescriptionIndex>(
-		                        boxes_.at(tab->key).get(), 0, 0, contents_width, contents_height))));
-		lists_.at(tab->key)->selected.connect(boost::bind(
-		   &EncyclopediaWindow::entry_selected, this, tab->key, tab->script_path, tab->type));
-
-		contents_.insert(
-		   std::make_pair(tab->key,
-		                  std::unique_ptr<UI::MultilineTextarea>(new UI::MultilineTextarea(
-		                     boxes_.at(tab->key).get(), 0, 0, contents_width, contents_height))));
-
-		boxes_.at(tab->key)->add(lists_.at(tab->key).get(), UI::Align::kLeft);
-		boxes_.at(tab->key)->add_space(kPadding);
-		boxes_.at(tab->key)->add(contents_.at(tab->key).get(), UI::Align::kLeft);
-
-		wrapper_boxes_.at(tab->key)->add_space(kPadding);
-		wrapper_boxes_.at(tab->key)->add(boxes_.at(tab->key).get(), UI::Align::kLeft);
-
-		tabs_.add("encyclopedia_" + tab->key,
-		          g_gr->images().get(tab->image_filename),
-		          wrapper_boxes_.at(tab->key).get(),
-		          tab->tooltip);
-	}
+	try {
+		set_title(table->get_string("title"));
+
+		// Read tab definitions
+		std::unique_ptr<LuaTable> tabs_table = table->get_table("tabs");
+		for (const auto& tab_table : tabs_table->array_entries<std::unique_ptr<LuaTable>>()) {
+			const std::string tab_name =  tab_table->get_string("name");
+			const std::string tab_icon = tab_table->has_key("icon") ? tab_table->get_string("icon") : "";
+			const std::string tab_title = tab_table->get_string("title");
+
+			wrapper_boxes_.insert(std::make_pair(
+				tab_name, std::unique_ptr<UI::Box>(new UI::Box(&tabs_, 0, 0, UI::Box::Horizontal))));
+
+			boxes_.insert(
+				std::make_pair(tab_name,
+									std::unique_ptr<UI::Box>(new UI::Box(
+										wrapper_boxes_.at(tab_name).get(), 0, 0, UI::Box::Horizontal))));
+
+			lists_.insert(
+				std::make_pair(tab_name,
+									std::unique_ptr<UI::Listselect<EncyclopediaEntry>>(
+										new UI::Listselect<EncyclopediaEntry>(
+											boxes_.at(tab_name).get(), 0, 0, contents_width, contents_height))));
+			lists_.at(tab_name)->selected.connect(boost::bind(&EncyclopediaWindow::entry_selected, this, tab_name));
+
+			contents_.insert(
+				std::make_pair(tab_name,
+									std::unique_ptr<UI::MultilineTextarea>(new UI::MultilineTextarea(
+										boxes_.at(tab_name).get(), 0, 0, contents_width, contents_height))));
+
+			boxes_.at(tab_name)->add(lists_.at(tab_name).get(), UI::Align::kLeft);
+			boxes_.at(tab_name)->add_space(kPadding);
+			boxes_.at(tab_name)->add(contents_.at(tab_name).get(), UI::Align::kLeft);
+
+			wrapper_boxes_.at(tab_name)->add_space(kPadding);
+			wrapper_boxes_.at(tab_name)->add(boxes_.at(tab_name).get(), UI::Align::kLeft);
+
+			if (tab_icon.empty()) {
+				tabs_.add("encyclopedia_" + tab_name,
+							 tab_title,
+							 wrapper_boxes_.at(tab_name).get());
+			} else if (g_fs->file_exists(tab_icon)) {
+				tabs_.add("encyclopedia_" + tab_name,
+							 g_gr->images().get(tab_icon),
+							 wrapper_boxes_.at(tab_name).get(),
+							 tab_title);
+			} else {
+				throw wexception("Icon path '%s' for tab '%s' does not exist!",
+									  tab_icon.c_str(),
+									  tab_name.c_str());
+			}
+
+			// Now fill the lists
+			std::unique_ptr<LuaTable> entries_table = tab_table->get_table("entries");
+			for (const auto& entry_table : entries_table->array_entries<std::unique_ptr<LuaTable>>()) {
+				const std::string entry_name =  entry_table->get_string("name");
+				const std::string entry_title =  entry_table->get_string("title");
+				const std::string entry_icon = entry_table->has_key("icon") ? entry_table->get_string("icon") : "";
+				const std::string entry_script =  entry_table->get_string("script");
+
+				// Make sure that all paths exist
+				if (!g_fs->file_exists(entry_script)) {
+					throw wexception("Script path %s for entry %s does not exist!",
+										  entry_script.c_str(),
+										  entry_name.c_str());
+				}
+
+				EncyclopediaEntry entry(entry_script, entry_table->get_table("script_parameters")->array_entries<std::string>());
+
+				if (entry_icon.empty()) {
+					lists_.at(tab_name)->add(entry_title, entry);
+				} else if (g_fs->file_exists(entry_icon)) {
+					lists_.at(tab_name)->add(entry_title, entry, g_gr->images().get(entry_icon));
+				} else {
+					throw wexception("Icon path '%s' for tab entry '%s' does not exist!",
+										  entry_icon.c_str(),
+										  entry_name.c_str());
+				}
+			}
+		}
+	} catch (WException& err) {
+		log("Error loading script for encyclopedia:\n%s\n", err.what());
+		UI::WLMessageBox wmb(
+					&parent,
+					_("Error!"),
+					(boost::format("Error loading script for encyclopedia:\n%s") % err.what()).str(),
+					UI::WLMessageBox::MBoxType::kOk);
+		wmb.run<UI::Panel::Returncodes>();
+	}
+
+	for (const auto& list : lists_) {
+		if (!(list.second->empty())) {
+			list.second->select(0);
+		}
+	}
+
 	tabs_.set_size(WINDOW_WIDTH, WINDOW_HEIGHT);
 
-	fill_buildings();
-	fill_wares();
-	fill_workers();
-
 	if (get_usedefaultpos()) {
 		center_to_parent();
 	}
 }
 
-void EncyclopediaWindow::fill_entries(const char* key, std::vector<EncyclopediaEntry>* entries) {
-	std::sort(entries->begin(), entries->end());
-	for (uint32_t i = 0; i < entries->size(); i++) {
-		EncyclopediaEntry cur = (*entries)[i];
-		lists_.at(key)->add(cur.descname, cur.index, cur.icon);
-	}
-	lists_.at(key)->select(0);
-}
-
-void EncyclopediaWindow::fill_buildings() {
-	const Widelands::Tribes& tribes = iaplayer().egbase().tribes();
-	const Widelands::TribeDescr& tribe = iaplayer().player().tribe();
-	std::vector<EncyclopediaEntry> entries;
-
-	for (Widelands::DescriptionIndex i = 0; i < tribes.nrbuildings(); ++i) {
-		const Widelands::BuildingDescr* building = tribes.get_building_descr(i);
-		if (tribe.has_building(i) || building->type() == Widelands::MapObjectType::MILITARYSITE) {
-			EncyclopediaEntry entry(i, building->descname(), building->icon());
-			entries.push_back(entry);
-		}
-	}
-	fill_entries("buildings", &entries);
-}
-
-void EncyclopediaWindow::fill_wares() {
-	const Widelands::TribeDescr& tribe = iaplayer().player().tribe();
-	std::vector<EncyclopediaEntry> entries;
-
-	for (const Widelands::DescriptionIndex& i : tribe.wares()) {
-		const Widelands::WareDescr* ware = tribe.get_ware_descr(i);
-		EncyclopediaEntry entry(i, ware->descname(), ware->icon());
-		entries.push_back(entry);
-	}
-	fill_entries("wares", &entries);
-}
-
-void EncyclopediaWindow::fill_workers() {
-	const Widelands::TribeDescr& tribe = iaplayer().player().tribe();
-	std::vector<EncyclopediaEntry> entries;
-
-	for (const Widelands::DescriptionIndex& i : tribe.workers()) {
-		const Widelands::WorkerDescr* worker = tribe.get_worker_descr(i);
-		EncyclopediaEntry entry(i, worker->descname(), worker->icon());
-		entries.push_back(entry);
-	}
-	fill_entries("workers", &entries);
-}
-
-void EncyclopediaWindow::entry_selected(const std::string& key,
-                                        const std::string& script_path,
-                                        const Widelands::MapObjectType& type) {
-	const Widelands::TribeDescr& tribe = iaplayer().player().tribe();
+void EncyclopediaWindow::entry_selected(const std::string& tab_name) {
+	const EncyclopediaEntry& entry = lists_.at(tab_name)->get_selected();
 	try {
-		std::unique_ptr<LuaTable> table(iaplayer().egbase().lua().run_script(script_path));
-		std::unique_ptr<LuaCoroutine> cr(table->get_coroutine("func"));
-		cr->push_arg(tribe.name());
-
-		std::string descname = "";
-
-		switch (type) {
-		case (Widelands::MapObjectType::BUILDING):
-		case (Widelands::MapObjectType::CONSTRUCTIONSITE):
-		case (Widelands::MapObjectType::DISMANTLESITE):
-		case (Widelands::MapObjectType::WAREHOUSE):
-		case (Widelands::MapObjectType::PRODUCTIONSITE):
-		case (Widelands::MapObjectType::MILITARYSITE):
-		case (Widelands::MapObjectType::TRAININGSITE): {
-			const Widelands::BuildingDescr* descr =
-			   tribe.get_building_descr(lists_.at(key)->get_selected());
-			descname = descr->descname();
-			cr->push_arg(descr);
-			break;
-		}
-		case (Widelands::MapObjectType::WARE): {
-			const Widelands::WareDescr* descr = tribe.get_ware_descr(lists_.at(key)->get_selected());
-			descname = descr->descname();
-			cr->push_arg(descr);
-			break;
-		}
-		case (Widelands::MapObjectType::WORKER):
-		case (Widelands::MapObjectType::CARRIER):
-		case (Widelands::MapObjectType::SOLDIER): {
-			const Widelands::WorkerDescr* descr =
-			   tribe.get_worker_descr(lists_.at(key)->get_selected());
-			descname = descr->descname();
-			cr->push_arg(descr);
-			break;
-		}
-		case (Widelands::MapObjectType::MAPOBJECT):
-		case (Widelands::MapObjectType::BATTLE):
-		case (Widelands::MapObjectType::FLEET):
-		case (Widelands::MapObjectType::BOB):
-		case (Widelands::MapObjectType::CRITTER):
-		case (Widelands::MapObjectType::SHIP):
-		case (Widelands::MapObjectType::IMMOVABLE):
-		case (Widelands::MapObjectType::FLAG):
-		case (Widelands::MapObjectType::ROAD):
-		case (Widelands::MapObjectType::PORTDOCK):
-			throw wexception("EncyclopediaWindow: No MapObjectType defined for tab.");
-		}
-
-		cr->resume();
-		const std::string help_text = cr->pop_string();
-		contents_.at(key)->set_text((boost::format("%s%s") % heading(descname) % help_text).str());
+		std::unique_ptr<LuaTable> table(lua_->run_script(entry.script_path));
+		if (!entry.script_parameters.empty()) {
+			std::unique_ptr<LuaCoroutine> cr(table->get_coroutine("func"));
+			for (const std::string& parameter : entry.script_parameters) {
+				cr->push_arg(parameter);
+			}
+			cr->resume();
+			table = cr->pop_table();
+		}
+		contents_.at(tab_name)->set_text((boost::format("%s%s")
+													 % heading(table->get_string("title"))
+													 % table->get_string("text")).str());
 	} catch (LuaError& err) {
-		contents_.at(key)->set_text(err.what());
+		contents_.at(tab_name)->set_text(err.what());
 	}
-	contents_.at(key)->scroll_to_top();
+	contents_.at(tab_name)->scroll_to_top();
 }
+
+} // namespace UI

=== renamed file 'src/wui/encyclopedia_window.h' => 'src/ui_basic/encyclopedia_window.h'
--- src/wui/encyclopedia_window.h	2016-02-14 14:09:29 +0000
+++ src/ui_basic/encyclopedia_window.h	2016-03-22 12:21:57 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2002-2004, 2006, 2009 by the Widelands Development Team
+ * Copyright (C) 2002-2016 by the Widelands Development Team
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -17,57 +17,46 @@
  *
  */
 
-#ifndef WL_WUI_ENCYCLOPEDIA_WINDOW_H
-#define WL_WUI_ENCYCLOPEDIA_WINDOW_H
+#ifndef WL_UI_BASIC_ENCYCLOPEDIA_WINDOW_H
+#define WL_UI_BASIC_ENCYCLOPEDIA_WINDOW_H
 
 #include <map>
 #include <memory>
+#include <vector>
 
-#include "logic/map_objects/map_object.h"
-#include "logic/map_objects/tribes/tribe_descr.h"
+#include "scripting/lua_interface.h"
+#include "scripting/lua_table.h"
 #include "ui_basic/box.h"
 #include "ui_basic/listselect.h"
 #include "ui_basic/multilinetextarea.h"
 #include "ui_basic/table.h"
 #include "ui_basic/tabpanel.h"
 #include "ui_basic/unique_window.h"
-#include "ui_basic/window.h"
-
-class InteractivePlayer;
+
+class InteractiveBase;
+
+namespace UI {
 
 struct EncyclopediaWindow : public UI::UniqueWindow {
-	EncyclopediaWindow(InteractivePlayer&, UI::UniqueWindow::Registry&);
+	EncyclopediaWindow(InteractiveBase&, UI::UniqueWindow::Registry&, LuaInterface* const lua);
+
+protected:
+	void init(InteractiveBase& parent, std::unique_ptr<LuaTable> table);
+
+	LuaInterface* const lua_;
 
 private:
 	struct EncyclopediaEntry {
-		EncyclopediaEntry(const EncyclopediaEntry&) = default;
-		EncyclopediaEntry& operator = (const EncyclopediaEntry&) = default;
-		EncyclopediaEntry(const Widelands::DescriptionIndex i,
-								const std::string& init_descname,
-								const Image* init_icon)
-			: index(i), descname(init_descname), icon(init_icon) {
-		}
-		Widelands::DescriptionIndex index;
-		std::string descname;
-		const Image* icon;
-
-		bool operator<(const EncyclopediaEntry other) const {
-			return descname < other.descname;
-		}
+		EncyclopediaEntry(const std::string& init_script_path,
+								const std::vector<std::string>& init_script_parameters)
+			: script_path(init_script_path), script_parameters(init_script_parameters) {
+		}
+		const std::string script_path;
+		const std::vector<std::string> script_parameters;
 	};
 
-	InteractivePlayer& iaplayer() const;
-
-	// Fill table of contents
-	void fill_entries(const char* key, std::vector<EncyclopediaEntry>* entries);
-	void fill_buildings();
-	void fill_wares();
-	void fill_workers();
-
 	// Update contents when an entry is selected
-	void entry_selected(const std::string& key,
-	                    const std::string& script_path,
-	                    const Widelands::MapObjectType& type);
+	void entry_selected(const std::string& tab_name);
 
 	// UI elements
 	UI::TabPanel tabs_;
@@ -77,9 +66,11 @@
 	// Main contents boxes for each tab
 	std::map<std::string, std::unique_ptr<UI::Box>> boxes_;
 	// A tab's table of contents
-	std::map<std::string, std::unique_ptr<UI::Listselect<Widelands::DescriptionIndex>>> lists_;
+	std::map<std::string, std::unique_ptr<UI::Listselect<EncyclopediaEntry>>> lists_;
 	// The contents shown when an entry is selected in a tab
 	std::map<std::string, std::unique_ptr<UI::MultilineTextarea>> contents_;
 };
 
-#endif  // end of include guard: WL_WUI_ENCYCLOPEDIA_WINDOW_H
+} // namespace UI
+
+#endif  // end of include guard: WL_UI_BASIC_ENCYCLOPEDIA_WINDOW_H

=== modified file 'src/wui/CMakeLists.txt'
--- src/wui/CMakeLists.txt	2016-02-06 18:58:57 +0000
+++ src/wui/CMakeLists.txt	2016-03-22 12:21:57 +0000
@@ -93,8 +93,6 @@
     debugconsole.cc
     debugconsole.h
     dismantlesitewindow.cc
-    encyclopedia_window.cc
-    encyclopedia_window.h
     fieldaction.cc
     fieldaction.h
     game_debug_ui.cc
@@ -161,6 +159,8 @@
     trainingsitewindow.cc
     transport_draw.cc
     transport_ui.cc
+    tribal_encyclopedia.cc
+    tribal_encyclopedia.h
     unique_window_handler.cc
     unique_window_handler.h
     ware_statistics_menu.cc
@@ -204,7 +204,6 @@
     scripting_lua_table
     sound
     ui_basic
-    ui_fsmenu
     widelands_ball_of_mud
     wui_chat_ui
     wui_edge_overlay_manager

=== modified file 'src/wui/game_options_menu.cc'
--- src/wui/game_options_menu.cc	2016-01-29 08:37:22 +0000
+++ src/wui/game_options_menu.cc	2016-03-22 12:21:57 +0000
@@ -27,7 +27,6 @@
 #include "base/i18n.h"
 #include "graphic/graphic.h"
 #include "sound/sound_handler.h"
-#include "ui_fsmenu/fileview.h"
 #include "wui/game_main_menu_save_game.h"
 #include "wui/game_options_sound_menu.h"
 #include "wui/unique_window_handler.h"
@@ -71,27 +70,6 @@
 	windows_(windows),
 	box_(this, margin, margin, UI::Box::Vertical,
 		  width, get_h() - 2 * margin, vspacing),
-	readme_
-		(&box_, "readme",
-		 0, 0, width, 0,
-		 g_gr->images().get("images/ui_basic/but4.png"),
-		 _("README"),
-		/** TRANSLATORS: Button tooltip */
-		_("Show general information about Widelands and keyboard shortcuts")),
-	license_
-		(&box_, "license",
-		 0, 0, width, 0,
-		 g_gr->images().get("images/ui_basic/but4.png"),
-		 _("License"),
-		/** TRANSLATORS: Button tooltip */
-		_("Show the distribution licence document")),
-	authors_
-		(&box_, "authors",
-		 0, 0, width, 0,
-		 g_gr->images().get("images/ui_basic/but4.png"),
-		 _("Authors"),
-		/** TRANSLATORS: Button tooltip */
-		_("Show information about the Widelands Development Team")),
 	sound_
 		(&box_, "sound_options",
 		 0, 0, width, 0,
@@ -114,49 +92,22 @@
 		 /** TRANSLATORS: Button tooltip */
 		 _("Exit Game"))
 {
-	box_.add(&readme_, UI::Align::kHCenter);
-	box_.add(&license_, UI::Align::kHCenter);
-	box_.add(&authors_, UI::Align::kHCenter);
-	box_.add_space(vgap);
 	box_.add(&sound_, UI::Align::kHCenter);
 	box_.add_space(vgap);
 	box_.add(&save_game_, UI::Align::kHCenter);
 	box_.add(&exit_game_, UI::Align::kHCenter);
-	box_.set_size(width, 4 * readme_.get_h() + 2 * save_game_.get_h() + 2 * vgap + 7 * vspacing);
+	box_.set_size(width, sound_.get_h() + 2 * save_game_.get_h() + vgap + 3 * vspacing);
 	set_inner_size(get_inner_w(), box_.get_h() + 2 * margin);
 
-	readme_.sigclicked.connect
-		(boost::bind(&UI::UniqueWindow::Registry::toggle, boost::ref(windows_.readme)));
-	license_.sigclicked.connect
-		(boost::bind(&UI::UniqueWindow::Registry::toggle, boost::ref(windows_.license)));
-	authors_.sigclicked.connect
-		(boost::bind(&UI::UniqueWindow::Registry::toggle, boost::ref(windows_.authors)));
 	sound_.sigclicked.connect(boost::bind(&GameOptionsMenu::clicked_sound, boost::ref(*this)));
 	save_game_.sigclicked.connect(boost::bind(&GameOptionsMenu::clicked_save_game, boost::ref(*this)));
 	exit_game_.sigclicked.connect(boost::bind(&GameOptionsMenu::clicked_exit_game, boost::ref(*this)));
 
-
-	windows_.readme.open_window = boost::bind
-		(&fileview_window, boost::ref(igb_),
-		 boost::ref(windows_.readme),
-		 "txts/README.lua");
-	windows_.license.open_window = boost::bind
-		(&fileview_window, boost::ref(igb_),
-		 boost::ref(windows_.license),
-		 "txts/LICENSE.lua");
-	windows_.authors.open_window = boost::bind
-		(&fileview_window, boost::ref(igb_),
-		 boost::ref(windows_.license),
-		 "txts/AUTHORS.lua");
-
 #define INIT_BTN_HOOKS(registry, btn)                                        \
  registry.on_create = std::bind(&UI::Button::set_perm_pressed, &btn, true);  \
  registry.on_delete = std::bind(&UI::Button::set_perm_pressed, &btn, false); \
  if (registry.window) btn.set_perm_pressed(true);                            \
 
-	INIT_BTN_HOOKS(windows_.readme, readme_)
-	INIT_BTN_HOOKS(windows_.license, license_)
-	INIT_BTN_HOOKS(windows_.authors, authors_)
 	INIT_BTN_HOOKS(windows_.sound_options, sound_)
 
 	if (get_usedefaultpos())

=== modified file 'src/wui/game_options_menu.h'
--- src/wui/game_options_menu.h	2015-09-25 13:08:22 +0000
+++ src/wui/game_options_menu.h	2016-03-22 12:21:57 +0000
@@ -39,9 +39,6 @@
 	InteractiveGameBase& igb_;
 	InteractiveGameBase::GameMainMenuWindows& windows_;
 	UI::Box box_;
-	UI::Button readme_;
-	UI::Button license_;
-	UI::Button authors_;
 	UI::Button sound_;
 	UI::Button save_game_;
 	UI::Button exit_game_;

=== modified file 'src/wui/helpwindow.cc'
--- src/wui/helpwindow.cc	2016-02-03 22:42:34 +0000
+++ src/wui/helpwindow.cc	2016-03-22 12:21:57 +0000
@@ -44,16 +44,18 @@
 			(boost::format(_("Help: %s")) % building_description.descname()).str()),
 	textarea_(new MultilineTextarea(this, 5, 5, width - 10, height - 10, std::string(), UI::Align::kLeft))
 {
-	assert(tribe.has_building(tribe.building_index(building_description.name())));
+	assert(tribe.has_building(tribe.building_index(building_description.name())) ||
+			 building_description.type() == Widelands::MapObjectType::MILITARYSITE);
 	try {
 		std::unique_ptr<LuaTable> t(
 		   lua->run_script("tribes/scripting/help/building_help.lua"));
 		std::unique_ptr<LuaCoroutine> cr(t->get_coroutine("func"));
 		cr->push_arg(tribe.name());
-		cr->push_arg(&building_description);
+		cr->push_arg(building_description.name());
 		cr->resume();
-		const std::string help_text = cr->pop_string();
-		textarea_->set_text(help_text);
+		std::unique_ptr<LuaTable> return_table = cr->pop_table();
+		return_table->do_not_warn_about_unaccessed_keys();
+		textarea_->set_text(return_table->get_string("text"));
 	} catch (LuaError& err) {
 		textarea_->set_text(err.what());
 	}

=== modified file 'src/wui/interactive_player.cc'
--- src/wui/interactive_player.cc	2016-02-09 16:29:48 +0000
+++ src/wui/interactive_player.cc	2016-03-22 12:21:57 +0000
@@ -44,7 +44,7 @@
 #include "ui_basic/unique_window.h"
 #include "wui/building_statistics_menu.h"
 #include "wui/debugconsole.h"
-#include "wui/encyclopedia_window.h"
+#include "wui/tribal_encyclopedia.h"
 #include "wui/fieldaction.h"
 #include "wui/game_chat_menu.h"
 #include "wui/game_main_menu.h"
@@ -156,7 +156,7 @@
 	INIT_BTN_HOOKS(encyclopedia_, toggle_help_)
 	INIT_BTN_HOOKS(message_menu_, toggle_message_menu_)
 
-	encyclopedia_.open_window = [this] {new EncyclopediaWindow(*this, encyclopedia_);};
+	encyclopedia_.open_window = [this] {new TribalEncyclopedia(*this, encyclopedia_, &game().lua());};
 	options_.open_window = [this] {new GameOptionsMenu(*this, options_, main_windows_);};
 	statisticsmenu_.open_window = [this] {
 		new GameMainMenu(*this, statisticsmenu_, main_windows_);

=== added file 'src/wui/tribal_encyclopedia.cc'
--- src/wui/tribal_encyclopedia.cc	1970-01-01 00:00:00 +0000
+++ src/wui/tribal_encyclopedia.cc	2016-03-22 12:21:57 +0000
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#include "wui/tribal_encyclopedia.h"
+
+#include <memory>
+
+#include <boost/format.hpp>
+
+#include "base/i18n.h"
+#include "logic/map_objects/tribes/tribe_descr.h"
+#include "logic/player.h"
+#include "scripting/lua_coroutine.h"
+#include "scripting/lua_interface.h"
+#include "scripting/lua_table.h"
+#include "ui_basic/messagebox.h"
+#include "wui/interactive_player.h"
+
+TribalEncyclopedia::TribalEncyclopedia(InteractivePlayer& parent, UI::UniqueWindow::Registry& registry, LuaInterface* const lua)
+	: EncyclopediaWindow(parent, registry, lua)
+{
+	const Widelands::TribeDescr& tribe = parent.player().tribe();
+	try {
+		std::unique_ptr<LuaTable> table(lua_->run_script("tribes/scripting/help/init.lua"));
+		std::unique_ptr<LuaCoroutine> cr(table->get_coroutine("func"));
+		cr->push_arg(tribe.name());
+		cr->resume();
+		init(parent, cr->pop_table());
+	} catch (LuaError& err) {
+		log("Error loading script for tribal encyclopedia:\n%s\n", err.what());
+		UI::WLMessageBox wmb(
+					&parent,
+					_("Error!"),
+					(boost::format("Error loading script for tribal encyclopedia:\n%s") % err.what()).str(),
+					UI::WLMessageBox::MBoxType::kOk);
+		wmb.run<UI::Panel::Returncodes>();
+	}
+}

=== added file 'src/wui/tribal_encyclopedia.h'
--- src/wui/tribal_encyclopedia.h	1970-01-01 00:00:00 +0000
+++ src/wui/tribal_encyclopedia.h	2016-03-22 12:21:57 +0000
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef WL_WUI_TRIBAL_ENCYCLOPEDIA_H
+#define WL_WUI_TRIBAL_ENCYCLOPEDIA_H
+
+#include "scripting/lua_interface.h"
+#include "ui_basic/encyclopedia_window.h"
+#include "ui_basic/unique_window.h"
+
+class InteractivePlayer;
+
+struct TribalEncyclopedia : public UI::EncyclopediaWindow {
+	TribalEncyclopedia(InteractivePlayer&, UI::UniqueWindow::Registry&, LuaInterface* const lua);
+};
+
+#endif  // end of include guard: WL_WUI_TRIBAL_ENCYCLOPEDIA_H

=== modified file 'test/maps/lua_testsuite.wmf/scripting/tribes_descriptions.lua'
--- test/maps/lua_testsuite.wmf/scripting/tribes_descriptions.lua	2015-10-31 12:11:44 +0000
+++ test/maps/lua_testsuite.wmf/scripting/tribes_descriptions.lua	2016-03-22 12:21:57 +0000
@@ -25,18 +25,27 @@
 end
 
 function test_descr:test_get_buildings()
-   local nobuildings = function(t)
-      local count = 0
-      for _ in pairs(t) do count = count + 1 end
-      return count
-   end
-
    local tribe = egbase:get_tribe_description("atlanteans")
-   assert_equal(41, nobuildings(tribe.buildings))
+   assert_equal(41, #tribe.buildings)
    tribe = egbase:get_tribe_description("barbarians")
-   assert_equal(50, nobuildings(tribe.buildings))
+   assert_equal(50, #tribe.buildings)
    tribe = egbase:get_tribe_description("empire")
-   assert_equal(51, nobuildings(tribe.buildings))
+   assert_equal(51, #tribe.buildings)
+
+   -- Test if buildings have been casted to their correct types
+   for i, building in ipairs(tribe.buildings) do
+      if (building.type_name == "productionsite") then
+         assert_true(#building.production_programs > 0)
+      elseif (building.type_name == "militarysite") then
+         assert_true(building.heal_per_second > 0)
+         assert_true(building.max_number_of_soldiers > 0)
+      elseif (building.type_name == "warehouse") then
+         assert_true(building.heal_per_second > 0)
+         assert_true(building.max_number_of_soldiers == nil)
+      elseif (building.type_name == "trainingsite") then
+         assert_true(building.max_attack ~= nil or building.max_defense ~= nil or building.max_evade ~= nil or building.max_health ~= nil)
+      end
+   end
 end
 
 function test_descr:test_get_carrier()
@@ -76,34 +85,20 @@
 
 function test_descr:test_get_wares()
    local tribe = egbase:get_tribe_description("atlanteans")
-   local nowares = function(t)
-      local count = 0
-      for _ in pairs(t) do count = count + 1 end
-      return count
-   end
-
-   local tribe = egbase:get_tribe_description("atlanteans")
-   assert_equal(44, nowares(tribe.wares))
+   assert_equal(44, #tribe.wares)
    tribe = egbase:get_tribe_description("barbarians")
-   assert_equal(40, nowares(tribe.wares))
+   assert_equal(40, #tribe.wares)
    tribe = egbase:get_tribe_description("empire")
-   assert_equal(44, nowares(tribe.wares))
+   assert_equal(44, #tribe.wares)
 end
 
 function test_descr:test_get_workers()
    local tribe = egbase:get_tribe_description("atlanteans")
-   local noworkers = function(t)
-      local count = 0
-      for _ in pairs(t) do count = count + 1 end
-      return count
-   end
-
-   local tribe = egbase:get_tribe_description("atlanteans")
-   assert_equal(29, noworkers(tribe.workers))
+   assert_equal(29, #tribe.workers)
    tribe = egbase:get_tribe_description("barbarians")
-   assert_equal(31, noworkers(tribe.workers))
+   assert_equal(31, #tribe.workers)
    tribe = egbase:get_tribe_description("empire")
-   assert_equal(32, noworkers(tribe.workers))
+   assert_equal(32, #tribe.workers)
 end
 
 function test_descr:test_has_building()


Follow ups