← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/constructionsite_options into lp:widelands

 

Benedikt Straub has proposed merging lp:~widelands-dev/widelands/constructionsite_options into lp:widelands.

Commit message:
Allow players to define settings for and to enhance buildings under construction

Requested reviews:
  Widelands Developers (widelands-dev)
Related bugs:
  Bug #1597310 in widelands: "Possibility to set building options when building is under construction"
  https://bugs.launchpad.net/widelands/+bug/1597310

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/constructionsite_options/+merge/367428
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/constructionsite_options into lp:widelands.
=== modified file 'src/ai/defaultai_seafaring.cc'
--- src/ai/defaultai_seafaring.cc	2019-02-23 11:00:49 +0000
+++ src/ai/defaultai_seafaring.cc	2019-05-14 17:38:23 +0000
@@ -346,7 +346,7 @@
 				if (site->bo->is(BuildingAttribute::kShipyard)) {
 					for (uint32_t k = 0; k < site->bo->inputs.size(); ++k) {
 						game().send_player_set_ware_priority(
-						   *site->site, wwWARE, site->bo->inputs.at(k), HIGH_PRIORITY);
+						   *site->site, wwWARE, site->bo->inputs.at(k), kPriorityHigh);
 					}
 				}
 			}

=== modified file 'src/economy/economy.cc'
--- src/economy/economy.cc	2019-04-09 16:43:49 +0000
+++ src/economy/economy.cc	2019-05-14 17:38:23 +0000
@@ -971,7 +971,7 @@
 static bool accept_warehouse_if_policy(Warehouse& wh,
                                        WareWorker type,
                                        DescriptionIndex ware,
-                                       Warehouse::StockPolicy policy) {
+                                       StockPolicy policy) {
 	return wh.get_stock_policy(type, ware) == policy;
 }
 
@@ -1005,8 +1005,8 @@
 
 		for (uint32_t nwh = 0; nwh < warehouses_.size(); ++nwh) {
 			Warehouse* wh = warehouses_[nwh];
-			Warehouse::StockPolicy policy = wh->get_stock_policy(type, ware);
-			if (policy == Warehouse::StockPolicy::kPrefer) {
+			StockPolicy policy = wh->get_stock_policy(type, ware);
+			if (policy == StockPolicy::kPrefer) {
 				haveprefer = true;
 
 				// Getting count of worker/ware
@@ -1022,7 +1022,7 @@
 					preferred_wh_stock = current_stock;
 				}
 			}
-			if (policy == Warehouse::StockPolicy::kNormal)
+			if (policy == StockPolicy::kNormal)
 				havenormal = true;
 		}
 		if (!havenormal && !haveprefer && type == wwWARE)
@@ -1037,7 +1037,7 @@
 			                            (!havenormal) ?
 			                               WarehouseAcceptFn() :
 			                               boost::bind(&accept_warehouse_if_policy, _1, type, ware,
-			                                           Warehouse::StockPolicy::kNormal));
+			                                           StockPolicy::kNormal));
 		}
 		if (!wh) {
 			log("Warning: Economy::handle_active_supplies "

=== modified file 'src/economy/request.cc'
--- src/economy/request.cc	2019-02-23 11:00:49 +0000
+++ src/economy/request.cc	2019-05-14 17:38:23 +0000
@@ -262,7 +262,7 @@
 int32_t Request::get_priority(int32_t cost) const {
 	int MAX_IDLE_PRIORITY = 100;
 	bool is_construction_site = false;
-	int32_t modifier = DEFAULT_PRIORITY;
+	int32_t modifier = kPriorityNormal;
 
 	if (target_building_) {
 		modifier = target_building_->get_priority(get_type(), get_index());

=== modified file 'src/logic/editor_game_base.cc'
--- src/logic/editor_game_base.cc	2019-04-26 05:52:49 +0000
+++ src/logic/editor_game_base.cc	2019-05-14 17:38:23 +0000
@@ -38,6 +38,7 @@
 #include "logic/map_objects/map_object.h"
 #include "logic/map_objects/tribes/battle.h"
 #include "logic/map_objects/tribes/building.h"
+#include "logic/map_objects/tribes/constructionsite.h"
 #include "logic/map_objects/tribes/dismantlesite.h"
 #include "logic/map_objects/tribes/tribe_descr.h"
 #include "logic/map_objects/tribes/tribes.h"
@@ -349,10 +350,15 @@
                                                 PlayerNumber const owner,
                                                 DescriptionIndex idx,
                                                 bool loading,
-                                                Building::FormerBuildings former_buildings) {
+                                                Building::FormerBuildings former_buildings,
+                                                const BuildingSettings* settings) {
 	Player* plr = get_player(owner);
 	const TribeDescr& tribe = plr->tribe();
-	return tribe.get_building_descr(idx)->create(*this, plr, c, true, loading, former_buildings);
+	Building& b = tribe.get_building_descr(idx)->create(*this, plr, c, true, loading, former_buildings);
+	if (settings) {
+		dynamic_cast<ConstructionSite&>(b).apply_settings(*settings);
+	}
+	return b;
 }
 
 /**

=== modified file 'src/logic/editor_game_base.h'
--- src/logic/editor_game_base.h	2019-02-27 19:00:36 +0000
+++ src/logic/editor_game_base.h	2019-05-14 17:38:23 +0000
@@ -56,6 +56,7 @@
 class TribeDescr;
 struct Flag;
 struct AttackController;
+struct BuildingSettings;
 
 struct NoteFieldPossession {
 	CAN_BE_SENT_AS_NOTE(NoteId::FieldPossession)
@@ -130,7 +131,8 @@
 	                      PlayerNumber,
 	                      DescriptionIndex,
 	                      bool loading = false,
-	                      Building::FormerBuildings former_buildings = Building::FormerBuildings());
+	                      Building::FormerBuildings former_buildings = Building::FormerBuildings(),
+	                      const BuildingSettings* settings = nullptr);
 	Building&
 	warp_dismantlesite(const Coords&,
 	                   PlayerNumber,

=== modified file 'src/logic/game.cc'
--- src/logic/game.cc	2019-05-11 18:19:20 +0000
+++ src/logic/game.cc	2019-05-14 17:38:23 +0000
@@ -56,6 +56,7 @@
 #include "logic/map_objects/tribes/soldier.h"
 #include "logic/map_objects/tribes/trainingsite.h"
 #include "logic/map_objects/tribes/tribe_descr.h"
+#include "logic/map_objects/tribes/warehouse.h"
 #include "logic/player.h"
 #include "logic/playercommand.h"
 #include "logic/replay.h"
@@ -859,6 +860,47 @@
 	   *new CmdProposeTrade(get_gametime(), object->get_owner()->player_number(), trade));
 }
 
+void Game::send_player_constructionsite_soldier_capacity(ConstructionSite& cs, uint32_t c) {
+	send_player_command(*new CmdConstructionsiteSoldierCapacity(
+		   get_gametime(), cs.get_owner()->player_number(), cs, c));
+}
+
+void Game::send_player_constructionsite_prefer_heroes(ConstructionSite& cs, bool h) {
+	send_player_command(*new CmdConstructionsitePreferHeroes(
+		   get_gametime(), cs.get_owner()->player_number(), cs, h));
+}
+
+void Game::send_player_constructionsite_launch_expedition(ConstructionSite& cs, bool l) {
+	send_player_command(*new CmdConstructionsiteLaunchExpedition(
+		   get_gametime(), cs.get_owner()->player_number(), cs, l));
+}
+
+void Game::send_player_constructionsite_stock_policy(
+		ConstructionSite& cs, WareWorker ww, DescriptionIndex di, StockPolicy pol) {
+	send_player_command(*new CmdConstructionsiteStockPolicy(
+		   get_gametime(), cs.get_owner()->player_number(), cs, ww, di, pol));
+}
+
+void Game::send_player_constructionsite_input_queue_priority(
+		ConstructionSite& cs, WareWorker ww, DescriptionIndex di, int32_t p) {
+	send_player_command(*new CmdConstructionsiteInputQueuePriority(
+		   get_gametime(), cs.get_owner()->player_number(), cs, ww, di, p));
+}
+
+void Game::send_player_constructionsite_input_queue_max_fill(
+		ConstructionSite& cs, WareWorker ww, DescriptionIndex di, uint32_t max) {
+	send_player_command(*new CmdConstructionsiteInputQueueMaxFill(
+		   get_gametime(), cs.get_owner()->player_number(), cs, ww, di, max));
+}
+
+void Game::send_player_constructionsite_enhance(ConstructionSite& cs) {
+	send_player_command(*new CmdConstructionsiteEnhance(get_gametime(), cs.get_owner()->player_number(), cs));
+}
+
+void Game::send_player_constructionsite_startstop(ConstructionSite& cs, bool stop) {
+	send_player_command(*new CmdConstructionsiteStartStop(get_gametime(), cs.get_owner()->player_number(), cs, stop));
+}
+
 int Game::propose_trade(const Trade& trade) {
 	// TODO(sirver,trading): Check if a trade is possible (i.e. if there is a
 	// path between the two markets);

=== modified file 'src/logic/game.h'
--- src/logic/game.h	2019-05-07 12:14:02 +0000
+++ src/logic/game.h	2019-05-14 17:38:23 +0000
@@ -46,6 +46,7 @@
 // See forester_cache_
 constexpr int16_t kInvalidForesterEntry = -1;
 
+class ConstructionSite;
 struct Flag;
 struct Path;
 struct PlayerImmovable;
@@ -56,6 +57,7 @@
 struct PlayerEndStatus;
 class TrainingSite;
 class MilitarySite;
+enum class StockPolicy;
 
 enum {
 	gs_notrunning = 0,  // game is being prepared
@@ -282,6 +284,16 @@
 	void send_player_cancel_expedition_ship(Ship&);
 	void send_player_propose_trade(const Trade& trade);
 
+	void send_player_constructionsite_soldier_capacity(ConstructionSite&, uint32_t);
+	void send_player_constructionsite_prefer_heroes(ConstructionSite&, bool);
+	void send_player_constructionsite_launch_expedition(ConstructionSite&, bool);
+	void send_player_constructionsite_stock_policy(
+			ConstructionSite&, WareWorker, DescriptionIndex, StockPolicy);
+	void send_player_constructionsite_input_queue_priority(ConstructionSite&, WareWorker, DescriptionIndex, int32_t);
+	void send_player_constructionsite_input_queue_max_fill(ConstructionSite&, WareWorker, DescriptionIndex, uint32_t);
+	void send_player_constructionsite_enhance(ConstructionSite&);
+	void send_player_constructionsite_startstop(ConstructionSite&, bool);
+
 	InteractivePlayer* get_ipl();
 
 	SaveHandler& save_handler() {

=== modified file 'src/logic/map_objects/CMakeLists.txt'
--- src/logic/map_objects/CMakeLists.txt	2019-05-05 18:53:14 +0000
+++ src/logic/map_objects/CMakeLists.txt	2019-05-14 17:38:23 +0000
@@ -49,6 +49,8 @@
     tribes/bill_of_materials.h
     tribes/building.cc
     tribes/building.h
+    tribes/building_settings.cc
+    tribes/building_settings.h
     tribes/carrier.cc
     tribes/carrier.h
     tribes/constructionsite.cc

=== modified file 'src/logic/map_objects/tribes/building.cc'
--- src/logic/map_objects/tribes/building.cc	2019-05-05 14:05:07 +0000
+++ src/logic/map_objects/tribes/building.cc	2019-05-14 17:38:23 +0000
@@ -638,13 +638,13 @@
 
 int32_t
 Building::get_priority(WareWorker type, DescriptionIndex const ware_index, bool adjust) const {
-	int32_t priority = DEFAULT_PRIORITY;
+	int32_t priority = kPriorityNormal;
 	if (type == wwWARE) {
 		// if priority is defined for specific ware,
 		// combine base priority and ware priority
 		std::map<DescriptionIndex, int32_t>::const_iterator it = ware_priorities_.find(ware_index);
 		if (it != ware_priorities_.end())
-			priority = adjust ? (priority * it->second / DEFAULT_PRIORITY) : it->second;
+			priority = adjust ? (priority * it->second / kPriorityNormal) : it->second;
 	}
 
 	return priority;
@@ -660,7 +660,7 @@
 	std::map<DescriptionIndex, int32_t>& ware_priorities = p[wwWARE];
 	std::map<DescriptionIndex, int32_t>::const_iterator it;
 	for (it = ware_priorities_.begin(); it != ware_priorities_.end(); ++it) {
-		if (it->second == DEFAULT_PRIORITY)
+		if (it->second == kPriorityNormal)
 			continue;
 		ware_priorities[it->first] = it->second;
 	}

=== modified file 'src/logic/map_objects/tribes/building.h'
--- src/logic/map_objects/tribes/building.h	2019-05-11 12:37:45 +0000
+++ src/logic/map_objects/tribes/building.h	2019-05-14 17:38:23 +0000
@@ -31,6 +31,7 @@
 #include "logic/map_objects/buildcost.h"
 #include "logic/map_objects/immovable.h"
 #include "logic/map_objects/tribes/attack_target.h"
+#include "logic/map_objects/tribes/building_settings.h"
 #include "logic/map_objects/tribes/bill_of_materials.h"
 #include "logic/map_objects/tribes/soldiercontrol.h"
 #include "logic/map_objects/tribes/wareworker.h"
@@ -51,9 +52,9 @@
 
 class Building;
 
-#define LOW_PRIORITY 2
-#define DEFAULT_PRIORITY 4
-#define HIGH_PRIORITY 8
+constexpr int32_t kPriorityLow = 2;
+constexpr int32_t kPriorityNormal = 4;
+constexpr int32_t kPriorityHigh = 8;
 
 /*
  * Common to all buildings!
@@ -266,8 +267,8 @@
 
 	// Get/Set the priority for this waretype for this building. 'type' defines
 	// if this is for a worker or a ware, 'index' is the type of worker or ware.
-	// If 'adjust' is false, the three possible states HIGH_PRIORITY,
-	// DEFAULT_PRIORITY and LOW_PRIORITY are returned, otherwise numerical
+	// If 'adjust' is false, the three possible states kPriorityHigh,
+	// kPriorityNormal and kPriorityLow are returned, otherwise numerical
 	// values adjusted to the preciousness of the ware in general are returned.
 	virtual int32_t get_priority(WareWorker type, DescriptionIndex, bool adjust = true) const;
 	void set_priority(int32_t type, DescriptionIndex ware_index, int32_t new_priority);
@@ -301,6 +302,10 @@
 	void add_worker(Worker&) override;
 	void remove_worker(Worker&) override;
 
+	virtual const BuildingSettings* create_building_settings() const {
+		return nullptr;
+	}
+
 	// AttackTarget object associated with this building. If the building can
 	// never be attacked (for example productionsites) this will be nullptr.
 	const AttackTarget* attack_target() const {

=== added file 'src/logic/map_objects/tribes/building_settings.cc'
--- src/logic/map_objects/tribes/building_settings.cc	1970-01-01 00:00:00 +0000
+++ src/logic/map_objects/tribes/building_settings.cc	2019-05-14 17:38:23 +0000
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2002-2019 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 "logic/map_objects/tribes/building_settings.h"
+
+#include "io/fileread.h"
+#include "io/filewrite.h"
+#include "logic/game.h"
+#include "logic/game_data_error.h"
+#include "logic/map_objects/tribes/militarysite.h"
+#include "logic/map_objects/tribes/productionsite.h"
+#include "logic/map_objects/tribes/trainingsite.h"
+#include "logic/map_objects/tribes/tribe_descr.h"
+#include "logic/map_objects/tribes/warehouse.h"
+
+namespace Widelands {
+
+ProductionsiteSettings::ProductionsiteSettings(const ProductionSiteDescr& descr)
+		: BuildingSettings(descr.name()), stopped(false) {
+	for (WareRange i(descr.input_wares()); i; ++i) {
+		ware_queues.push_back(std::make_pair(i.current->first,
+				InputQueueSetting{i.current->second, i.current->second, kPriorityNormal}));
+	}
+	for (WareRange i(descr.input_workers()); i; ++i) {
+		worker_queues.push_back(std::make_pair(i.current->first,
+				InputQueueSetting{i.current->second, i.current->second, kPriorityNormal}));
+	}
+}
+
+MilitarysiteSettings::MilitarysiteSettings(const MilitarySiteDescr& descr)
+		: BuildingSettings(descr.name()),
+	  max_capacity(descr.get_max_number_of_soldiers()),
+	  desired_capacity(descr.get_max_number_of_soldiers()),
+	  prefer_heroes(descr.prefers_heroes_at_start_) {
+}
+
+TrainingsiteSettings::TrainingsiteSettings(const TrainingSiteDescr& descr)
+	: ProductionsiteSettings(descr),
+	  max_capacity(descr.get_max_number_of_soldiers()),
+	  desired_capacity(descr.get_max_number_of_soldiers()) {
+}
+
+WarehouseSettings::WarehouseSettings(const WarehouseDescr& wh, const TribeDescr& tribe)
+		: BuildingSettings(wh.name()), launch_expedition_allowed(wh.get_isport()), launch_expedition(false) {
+	for (const DescriptionIndex di : tribe.wares()) {
+		ware_preferences.emplace(di, StockPolicy::kNormal);
+	}
+	for (const DescriptionIndex di : tribe.workers()) {
+		worker_preferences.emplace(di, StockPolicy::kNormal);
+	}
+	for (const DescriptionIndex di : tribe.worker_types_without_cost()) {
+		worker_preferences.erase(di);
+	}
+}
+
+void ProductionsiteSettings::apply(const BuildingSettings& bs) {
+	BuildingSettings::apply(bs);
+	if (upcast(const ProductionsiteSettings, s, &bs)) {
+		stopped = s->stopped;
+		for (auto& pair : ware_queues) {
+			for (const auto& other : s->ware_queues) {
+				if (pair.first == other.first) {
+					pair.second.priority = other.second.priority;
+					pair.second.desired_fill = std::min(pair.second.max_fill, other.second.desired_fill);
+					break;
+				}
+			}
+		}
+		for (auto& pair : worker_queues) {
+			for (const auto& other : s->worker_queues) {
+				if (pair.first == other.first) {
+					pair.second.priority = other.second.priority;
+					pair.second.desired_fill = std::min(pair.second.max_fill, other.second.desired_fill);
+					break;
+				}
+			}
+		}
+	}
+}
+
+void TrainingsiteSettings::apply(const BuildingSettings& bs) {
+	ProductionsiteSettings::apply(bs);
+	if (upcast(const TrainingsiteSettings, s, &bs)) {
+		desired_capacity = std::min(max_capacity, s->desired_capacity);
+	}
+}
+
+void MilitarysiteSettings::apply(const BuildingSettings& bs) {
+	BuildingSettings::apply(bs);
+	if (upcast(const MilitarysiteSettings, s, &bs)) {
+		desired_capacity = std::min(max_capacity, s->desired_capacity);
+		prefer_heroes = s->prefer_heroes;
+	}
+}
+
+void WarehouseSettings::apply(const BuildingSettings& bs) {
+	BuildingSettings::apply(bs);
+	if (upcast(const WarehouseSettings, s, &bs)) {
+		for (auto& pair : ware_preferences) {
+			const auto it = s->ware_preferences.find(pair.first);
+			if (it != s->ware_preferences.end()) {
+				pair.second = it->second;
+			}
+		}
+		for (auto& pair : worker_preferences) {
+			const auto it = s->worker_preferences.find(pair.first);
+			if (it != s->worker_preferences.end()) {
+				pair.second = it->second;
+			}
+		}
+		launch_expedition = launch_expedition_allowed && s->launch_expedition;
+	}
+}
+
+// Saveloading
+
+constexpr uint8_t kCurrentPacketVersion = 1;
+constexpr uint8_t kCurrentPacketVersionMilitarysite = 1;
+constexpr uint8_t kCurrentPacketVersionProductionsite = 1;
+constexpr uint8_t kCurrentPacketVersionTrainingsite = 1;
+constexpr uint8_t kCurrentPacketVersionWarehouse = 1;
+
+enum class BuildingType : uint8_t {
+	kWarehouse = 1,
+	kProductionsite = 2,
+	kTrainingsite = 3,
+	kMilitarysite = 4,
+};
+
+// static
+BuildingSettings* BuildingSettings::load(const Game& game, const TribeDescr& tribe, FileRead& fr) {
+	try {
+		const uint8_t packet_version = fr.unsigned_8();
+		if (packet_version == kCurrentPacketVersion) {
+			const std::string name(fr.c_string());
+			const DescriptionIndex index = game.tribes().building_index(name);
+			const BuildingType type = static_cast<BuildingType>(fr.unsigned_8());
+			BuildingSettings* result = nullptr;
+			switch (type) {
+				case BuildingType::kTrainingsite: {
+					result = new TrainingsiteSettings(*dynamic_cast<const TrainingSiteDescr*>(
+							game.tribes().get_building_descr(index)));
+					break;
+				}
+				case BuildingType::kProductionsite: {
+					result = new ProductionsiteSettings(*dynamic_cast<const ProductionSiteDescr*>(
+							game.tribes().get_building_descr(index)));
+					break;
+				}
+				case BuildingType::kMilitarysite: {
+					result = new MilitarysiteSettings(*dynamic_cast<const MilitarySiteDescr*>(
+							game.tribes().get_building_descr(index)));
+					break;
+				}
+				case BuildingType::kWarehouse: {
+					result = new WarehouseSettings(*dynamic_cast<const WarehouseDescr*>(
+							game.tribes().get_building_descr(index)), tribe);
+					break;
+				}
+				default:
+					throw wexception("Unknown building category %u (%s)", static_cast<uint8_t>(type), name.c_str());
+			}
+			result->read(game, fr);
+			return result;
+		} else {
+			throw UnhandledVersionError(
+			   "BuildingSettings_load", packet_version, kCurrentPacketVersion);
+		}
+	} catch (const WException& e) {
+		throw GameDataError("BuildingSettings_load: %s", e.what());
+	}
+	NEVER_HERE();
+}
+
+void BuildingSettings::read(const Game&, FileRead&) {
+	// Header was peeled away by load()
+}
+
+void BuildingSettings::save(const Game&, FileWrite& fw) const {
+	fw.unsigned_8(kCurrentPacketVersion);
+	fw.c_string(descr_.c_str());
+}
+
+void MilitarysiteSettings::read(const Game& game, FileRead& fr) {
+	BuildingSettings::read(game, fr);
+	try {
+		const uint8_t packet_version = fr.unsigned_8();
+		if (packet_version == kCurrentPacketVersionMilitarysite) {
+			desired_capacity = fr.unsigned_32();
+			prefer_heroes = fr.unsigned_8();
+		} else {
+			throw UnhandledVersionError(
+			   "MilitarysiteSettings", packet_version, kCurrentPacketVersionMilitarysite);
+		}
+	} catch (const WException& e) {
+		throw GameDataError("MilitarysiteSettings: %s", e.what());
+	}
+}
+
+void MilitarysiteSettings::save(const Game& game, FileWrite& fw) const {
+	BuildingSettings::save(game, fw);
+	fw.unsigned_8(static_cast<uint8_t>(BuildingType::kMilitarysite));
+	fw.unsigned_8(kCurrentPacketVersionMilitarysite);
+
+	fw.unsigned_32(desired_capacity);
+	fw.unsigned_8(prefer_heroes ? 1 : 0);
+}
+
+void ProductionsiteSettings::read(const Game& game, FileRead& fr) {
+	BuildingSettings::read(game, fr);
+	try {
+		const uint8_t packet_version = fr.unsigned_8();
+		if (packet_version == kCurrentPacketVersionProductionsite) {
+			stopped = fr.unsigned_8();
+			const uint32_t nr_wares = fr.unsigned_32();
+			const uint32_t nr_workers = fr.unsigned_32();
+			for (uint32_t i = 0; i < nr_wares; ++i) {
+				const DescriptionIndex di = fr.unsigned_32();
+				const uint32_t fill = fr.unsigned_32();
+				const int32_t priority = fr.signed_32();
+				ware_queues.at(i).first = di;
+				ware_queues.at(i).second.desired_fill = fill;
+				ware_queues.at(i).second.priority = priority;
+			}
+			for (uint32_t i = 0; i < nr_workers; ++i) {
+				const DescriptionIndex di = fr.unsigned_32();
+				const uint32_t fill = fr.unsigned_32();
+				const int32_t priority = fr.signed_32();
+				worker_queues.at(i).first = di;
+				worker_queues.at(i).second.desired_fill = fill;
+				worker_queues.at(i).second.priority = priority;
+			}
+		} else {
+			throw UnhandledVersionError(
+			   "ProductionsiteSettings", packet_version, kCurrentPacketVersionProductionsite);
+		}
+	} catch (const WException& e) {
+		throw GameDataError("ProductionsiteSettings: %s", e.what());
+	}
+}
+
+void ProductionsiteSettings::save(const Game& game, FileWrite& fw) const {
+	BuildingSettings::save(game, fw);
+	fw.unsigned_8(static_cast<uint8_t>(BuildingType::kProductionsite));
+	fw.unsigned_8(kCurrentPacketVersionProductionsite);
+
+	fw.unsigned_8(stopped ? 1 : 0);
+	fw.unsigned_32(ware_queues.size());
+	fw.unsigned_32(worker_queues.size());
+	for (const auto& pair : ware_queues) {
+		fw.unsigned_32(pair.first);
+		fw.unsigned_32(pair.second.desired_fill);
+		fw.signed_32(pair.second.priority);
+	}
+	for (const auto& pair : worker_queues) {
+		fw.unsigned_32(pair.first);
+		fw.unsigned_32(pair.second.desired_fill);
+		fw.signed_32(pair.second.priority);
+	}
+}
+
+void TrainingsiteSettings::read(const Game& game, FileRead& fr) {
+	ProductionsiteSettings::read(game, fr);
+	try {
+		const uint8_t packet_version = fr.unsigned_8();
+		if (packet_version == kCurrentPacketVersionTrainingsite) {
+			desired_capacity = fr.unsigned_32();
+		} else {
+			throw UnhandledVersionError(
+			   "TrainingsiteSettings", packet_version, kCurrentPacketVersionTrainingsite);
+		}
+	} catch (const WException& e) {
+		throw GameDataError("TrainingsiteSettings: %s", e.what());
+	}
+}
+
+void TrainingsiteSettings::save(const Game& game, FileWrite& fw) const {
+	ProductionsiteSettings::save(game, fw);
+	fw.unsigned_8(static_cast<uint8_t>(BuildingType::kTrainingsite));
+	fw.unsigned_8(kCurrentPacketVersionTrainingsite);
+	fw.unsigned_32(desired_capacity);
+}
+
+void WarehouseSettings::read(const Game& game, FileRead& fr) {
+	BuildingSettings::read(game, fr);
+	try {
+		const uint8_t packet_version = fr.unsigned_8();
+		if (packet_version == kCurrentPacketVersionWarehouse) {
+			launch_expedition = fr.unsigned_8();
+			const uint32_t nr_wares = fr.unsigned_32();
+			const uint32_t nr_workers = fr.unsigned_32();
+			for (uint32_t i = 0; i < nr_wares; ++i) {
+				const DescriptionIndex di = fr.unsigned_32();
+				const uint8_t pref = fr.unsigned_8();
+				ware_preferences[di] = static_cast<StockPolicy>(pref);
+			}
+			for (uint32_t i = 0; i < nr_workers; ++i) {
+				const DescriptionIndex di = fr.unsigned_32();
+				const uint8_t pref = fr.unsigned_8();
+				worker_preferences[di] = static_cast<StockPolicy>(pref);
+			}
+		} else {
+			throw UnhandledVersionError(
+			   "WarehouseSettings", packet_version, kCurrentPacketVersionWarehouse);
+		}
+	} catch (const WException& e) {
+		throw GameDataError("WarehouseSettings: %s", e.what());
+	}
+}
+
+void WarehouseSettings::save(const Game& game, FileWrite& fw) const {
+	BuildingSettings::save(game, fw);
+	fw.unsigned_8(static_cast<uint8_t>(BuildingType::kWarehouse));
+	fw.unsigned_8(kCurrentPacketVersionWarehouse);
+
+	fw.unsigned_8(launch_expedition ? 1 : 0);
+	fw.unsigned_32(ware_preferences.size());
+	fw.unsigned_32(worker_preferences.size());
+	for (const auto& pair : ware_preferences) {
+		fw.unsigned_32(pair.first);
+		fw.unsigned_8(static_cast<uint8_t>(pair.second));
+	}
+	for (const auto& pair : worker_preferences) {
+		fw.unsigned_32(pair.first);
+		fw.unsigned_8(static_cast<uint8_t>(pair.second));
+	}
+}
+
+} // namespace Widelands

=== added file 'src/logic/map_objects/tribes/building_settings.h'
--- src/logic/map_objects/tribes/building_settings.h	1970-01-01 00:00:00 +0000
+++ src/logic/map_objects/tribes/building_settings.h	2019-05-14 17:38:23 +0000
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2002-2019 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_LOGIC_MAP_OBJECTS_TRIBES_BUILDING_SETTINGS_H
+#define WL_LOGIC_MAP_OBJECTS_TRIBES_BUILDING_SETTINGS_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "logic/widelands.h"
+
+class FileRead;
+class FileWrite;
+
+namespace Widelands {
+
+class Game;
+class MilitarySiteDescr;
+class ProductionSiteDescr;
+enum class StockPolicy;
+class TrainingSiteDescr;
+class TribeDescr;
+class WarehouseDescr;
+
+struct BuildingSettings {
+	BuildingSettings(const std::string& name) : descr_(name) {
+	}
+	static BuildingSettings* load(const Game&, const TribeDescr&, FileRead&);
+
+	virtual void save(const Game&, FileWrite&) const;
+	virtual void read(const Game&, FileRead&);
+
+	virtual void apply(const BuildingSettings&) {
+	}
+
+private:
+	const std::string descr_;
+};
+
+struct ProductionsiteSettings : public BuildingSettings {
+	ProductionsiteSettings(const ProductionSiteDescr& descr);
+	void apply(const BuildingSettings&) override;
+
+	void save(const Game&, FileWrite&) const override;
+	void read(const Game&, FileRead&) override;
+
+	struct InputQueueSetting {
+		const uint32_t max_fill;
+		uint32_t desired_fill;
+		int32_t priority;
+	};
+	std::vector<std::pair<DescriptionIndex, InputQueueSetting>> ware_queues;
+	std::vector<std::pair<DescriptionIndex, InputQueueSetting>> worker_queues;
+	bool stopped;
+};
+
+struct MilitarysiteSettings : public BuildingSettings {
+	MilitarysiteSettings(const MilitarySiteDescr&);
+	void apply(const BuildingSettings&) override;
+
+	void save(const Game&, FileWrite&) const override;
+	void read(const Game&, FileRead&) override;
+
+	const uint32_t max_capacity;
+	uint32_t desired_capacity;
+	bool prefer_heroes;
+};
+
+struct TrainingsiteSettings : public ProductionsiteSettings {
+	TrainingsiteSettings(const TrainingSiteDescr&);
+	void apply(const BuildingSettings&) override;
+
+	void save(const Game&, FileWrite&) const override;
+	void read(const Game&, FileRead&) override;
+
+	const uint32_t max_capacity;
+	uint32_t desired_capacity;
+};
+
+struct WarehouseSettings : public BuildingSettings {
+	WarehouseSettings(const WarehouseDescr&, const TribeDescr&);
+	void apply(const BuildingSettings&) override;
+
+	void save(const Game&, FileWrite&) const override;
+	void read(const Game&, FileRead&) override;
+
+	std::map<DescriptionIndex, StockPolicy> ware_preferences;
+	std::map<DescriptionIndex, StockPolicy> worker_preferences;
+	const bool launch_expedition_allowed;
+	bool launch_expedition;
+};
+
+}  // namespace Widelands
+
+#endif  // end of include guard: WL_LOGIC_MAP_OBJECTS_TRIBES_BUILDING_SETTINGS_H

=== modified file 'src/logic/map_objects/tribes/constructionsite.cc'
--- src/logic/map_objects/tribes/constructionsite.cc	2019-04-26 05:52:49 +0000
+++ src/logic/map_objects/tribes/constructionsite.cc	2019-05-14 17:38:23 +0000
@@ -33,6 +33,11 @@
 #include "graphic/text_constants.h"
 #include "logic/editor_game_base.h"
 #include "logic/game.h"
+#include "logic/game_data_error.h"
+#include "logic/map_objects/tribes/militarysite.h"
+#include "logic/map_objects/tribes/partially_finished_building.h"
+#include "logic/map_objects/tribes/productionsite.h"
+#include "logic/map_objects/tribes/trainingsite.h"
 #include "logic/map_objects/tribes/tribe_descr.h"
 #include "logic/map_objects/tribes/worker.h"
 #include "sound/note_sound.h"
@@ -47,32 +52,67 @@
                                        const RGBColor& player_color,
                                        RenderTarget* dst) const {
 	// Draw the construction site marker
-	const uint32_t anim_idx = becomes->is_animation_known("build") ?
-	                             becomes->get_animation("build") :
-	                             becomes->get_unoccupied_animation();
-
-	const Animation& anim = g_gr->animations().get_animation(anim_idx);
-	const size_t nr_frames = anim.nr_frames();
-	const uint32_t cur_frame = totaltime ? completedtime * nr_frames / totaltime : 0;
-	uint32_t anim_time = cur_frame * FRAME_LENGTH;
-
-	if (cur_frame) {  //  not the first pic
-		// Draw the complete prev pic , so we won't run into trouble if images have different sizes
-		dst->blit_animation(point_on_dst, Widelands::Coords::null(), scale, anim_idx,
-		                    anim_time - FRAME_LENGTH, &player_color);
+	std::vector<std::pair<uint32_t, uint32_t>> animations;
+	uint32_t total_frames = 0;
+	for (const BuildingDescr* d : intermediates) {
+		const bool known = d->is_animation_known("build");
+		const uint32_t anim_idx = known ?
+				d->get_animation("build") :
+				d->get_unoccupied_animation();
+		// If there is no build animation, we use only the first frame or we
+		// would get many build steps with almost the same image...
+		const uint32_t nrframes = known ? g_gr->animations().get_animation(anim_idx).nr_frames() : 1;
+		assert(nrframes);
+		total_frames += nrframes;
+		animations.push_back(std::make_pair(anim_idx, nrframes));
+	}
+	{ // Now the same for the final building
+		const bool known = becomes->is_animation_known("build");
+		const uint32_t anim_idx = known ?
+				becomes->get_animation("build") :
+				becomes->get_unoccupied_animation();
+		const uint32_t nrframes = known ? g_gr->animations().get_animation(anim_idx).nr_frames() : 1;
+		assert(nrframes);
+		total_frames += nrframes;
+		animations.push_back(std::make_pair(anim_idx, nrframes));
+	}
+
+	uint32_t frame_index = totaltime ? std::min(completedtime * total_frames / totaltime, total_frames) : 0;
+	uint32_t animation_index = 0;
+	while (frame_index >= animations[animation_index].second) {
+		frame_index -= animations[animation_index].second;
+		++animation_index;
+		assert(animation_index < animations.size());
+	}
+	const uint32_t anim_time = frame_index * FRAME_LENGTH;
+
+	if (frame_index > 0) {
+		// Not the first pic within this animation – draw the previous one
+		dst->blit_animation(point_on_dst, Widelands::Coords::null(), scale,
+				animations[animation_index].first, anim_time - FRAME_LENGTH, &player_color);
+	} else if (animation_index > 0) {
+		// The first pic, but not the first series of animations – draw the last pic of the previous series
+		dst->blit_animation(point_on_dst, Widelands::Coords::null(), scale,
+				animations[animation_index - 1].first,
+				FRAME_LENGTH * (animations[animation_index - 1].second - 1), &player_color);
 	} else if (was) {
-		//  Is the first picture but there was another building here before,
-		//  get its most fitting picture and draw it instead.
-		dst->blit_animation(point_on_dst, Widelands::Coords::null(), scale,
-		                    was->get_unoccupied_animation(), anim_time - FRAME_LENGTH, &player_color);
+		//  First pic in first series, but there was another building here before –
+		//  get its most fitting picture and draw it instead
+		const uint32_t unocc = was->get_unoccupied_animation();
+		dst->blit_animation(point_on_dst, Widelands::Coords::null(), scale, unocc,
+				FRAME_LENGTH * (g_gr->animations().get_animation(unocc).nr_frames() - 1),
+				&player_color);
 	}
 	// Now blit a segment of the current construction phase from the bottom.
-	int percent = 100 * completedtime * nr_frames;
+	int percent = 100 * completedtime * total_frames;
 	if (totaltime) {
 		percent /= totaltime;
 	}
-	percent -= 100 * cur_frame;
-	dst->blit_animation(point_on_dst, coords, scale, anim_idx, anim_time, &player_color, percent);
+	percent -= 100 * frame_index;
+	for (uint32_t i = 0; i < animation_index; ++i) {
+		percent -= 100 * animations[i].second;
+	}
+	dst->blit_animation(point_on_dst, coords, scale, animations[animation_index].first, anim_time, &player_color, percent);
 }
 
 /**
@@ -105,7 +145,7 @@
 */
 
 ConstructionSite::ConstructionSite(const ConstructionSiteDescr& cs_descr)
-   : PartiallyFinishedBuilding(cs_descr), fetchfromflag_(0), builder_idle_(false) {
+   : PartiallyFinishedBuilding(cs_descr), fetchfromflag_(0), builder_idle_(false), settings_(nullptr) {
 }
 
 void ConstructionSite::update_statistics_string(std::string* s) {
@@ -183,6 +223,22 @@
 
 		work_steps_ += it->second;
 	}
+
+	assert(!settings_);
+	if (upcast(const WarehouseDescr, wd, building_)) {
+		settings_.reset(new WarehouseSettings(*wd, owner().tribe()));
+	} else if (upcast(const TrainingSiteDescr, td, building_)) {
+		settings_.reset(new TrainingsiteSettings(*td));
+	} else if (upcast(const ProductionSiteDescr, pd, building_)) {
+		settings_.reset(new ProductionsiteSettings(*pd));
+	} else if (upcast(const MilitarySiteDescr, md, building_)) {
+		settings_.reset(new MilitarysiteSettings(*md));
+	} else {
+		// TODO(Nordfriese): Add support for markets when trading is implemented
+		log("WARNING: Created constructionsite for a %s, which is not of any known building type\n",
+				building_->name().c_str());
+	}
+
 	return true;
 }
 
@@ -209,6 +265,48 @@
 			builder->reset_tasks(dynamic_cast<Game&>(egbase));
 			builder->set_location(&b);
 		}
+
+		// Apply settings
+		if (settings_) {
+			if (upcast(ProductionsiteSettings, ps, settings_.get())) {
+				for (const auto& pair : ps->ware_queues) {
+					b.inputqueue(pair.first, wwWARE).set_max_fill(pair.second.desired_fill);
+					b.set_priority(wwWARE, pair.first, pair.second.priority);
+				}
+				for (const auto& pair : ps->worker_queues) {
+					b.inputqueue(pair.first, wwWORKER).set_max_fill(pair.second.desired_fill);
+					b.set_priority(wwWORKER, pair.first, pair.second.priority);
+				}
+				if (upcast(TrainingsiteSettings, ts, ps)) {
+					assert(b.soldier_control());
+					assert(ts->desired_capacity >= b.soldier_control()->min_soldier_capacity());
+					assert(ts->desired_capacity <= b.soldier_control()->max_soldier_capacity());
+					b.mutable_soldier_control()->set_soldier_capacity(ts->desired_capacity);
+				}
+				dynamic_cast<ProductionSite&>(b).set_stopped(ps->stopped);
+			} else if (upcast(MilitarysiteSettings, ms, settings_.get())) {
+				assert(b.soldier_control());
+				assert(ms->desired_capacity >= b.soldier_control()->min_soldier_capacity());
+				assert(ms->desired_capacity <= b.soldier_control()->max_soldier_capacity());
+				b.mutable_soldier_control()->set_soldier_capacity(ms->desired_capacity);
+				dynamic_cast<MilitarySite&>(b).set_soldier_preference(ms->prefer_heroes ?
+						SoldierPreference::kHeroes : SoldierPreference::kRookies);
+			} else if (upcast(WarehouseSettings, ws, settings_.get())) {
+				Warehouse& site = dynamic_cast<Warehouse&>(b);
+				for (const auto& pair : ws->ware_preferences) {
+					site.set_ware_policy(pair.first, pair.second);
+				}
+				for (const auto& pair : ws->worker_preferences) {
+					site.set_worker_policy(pair.first, pair.second);
+				}
+				if (ws->launch_expedition) {
+					get_owner()->start_or_cancel_expedition(site);
+				}
+			} else {
+				NEVER_HERE();
+			}
+		}
+
 		// Open the new building window if needed
 		Notifications::publish(NoteBuilding(b.serial(), NoteBuilding::Action::kFinishWarp));
 	}
@@ -216,6 +314,138 @@
 
 /*
 ===============
+Start building the next enhancement even before the base building is completed.
+===============
+*/
+void ConstructionSite::enhance(Game&) {
+	assert(building_->enhancement() != INVALID_INDEX);
+	Notifications::publish(NoteImmovable(this, NoteImmovable::Ownership::LOST));
+
+	info_.intermediates.push_back(building_);
+	old_buildings_.push_back(owner().tribe().building_index(building_->name()));
+	building_ = owner().tribe().get_building_descr(building_->enhancement());
+	assert(building_);
+	info_.becomes = building_;
+
+	const std::map<DescriptionIndex, uint8_t>& buildcost = building_->enhancement_cost();
+	const size_t buildcost_size = buildcost.size();
+	std::set<DescriptionIndex> new_ware_types;
+	for (std::map<DescriptionIndex, uint8_t>::const_iterator it = buildcost.begin(); it != buildcost.end(); ++it) {
+		bool found = false;
+		for (const auto& queue : wares_) {
+			if (queue->get_index() == it->first) {
+				found = true;
+				break;
+			}
+		}
+		if (!found) {
+			new_ware_types.insert(it->first);
+		}
+	}
+
+	const size_t old_size = wares_.size();
+	wares_.resize(old_size + new_ware_types.size());
+	std::map<DescriptionIndex, uint8_t>::const_iterator it = buildcost.begin();
+
+	size_t new_wares_index = 0;
+	for (size_t i = 0; i < buildcost_size; ++i, ++it) {
+		if (new_ware_types.count(it->first)) {
+			WaresQueue& wq = *(wares_[old_size + new_wares_index] = new WaresQueue(*this, it->first, it->second));
+			wq.set_callback(ConstructionSite::wares_queue_callback, this);
+			wq.set_consume_interval(CONSTRUCTIONSITE_STEP_TIME);
+			++new_wares_index;
+		} else {
+			assert(i >= new_wares_index);
+			WaresQueue& wq = *wares_[i - new_wares_index];
+			wq.set_max_size(wq.get_max_size() + it->second);
+			wq.set_max_fill(wq.get_max_fill() + it->second);
+		}
+		work_steps_ += it->second;
+	}
+
+	BuildingSettings* old_settings = settings_.release();
+	if (upcast(const WarehouseDescr, wd, building_)) {
+		WarehouseSettings* new_settings = new WarehouseSettings(*wd, owner().tribe());
+		settings_.reset(new_settings);
+		if (upcast(WarehouseSettings, ws, old_settings)) {
+			for (const auto& pair : ws->ware_preferences) {
+				new_settings->ware_preferences[pair.first] = pair.second;
+			}
+			for (const auto& pair : ws->worker_preferences) {
+				new_settings->worker_preferences[pair.first] = pair.second;
+			}
+			new_settings->launch_expedition = ws->launch_expedition && building_->get_isport();
+		}
+	} else if (upcast(const TrainingSiteDescr, td, building_)) {
+		TrainingsiteSettings* new_settings = new TrainingsiteSettings(*td);
+		settings_.reset(new_settings);
+		if (upcast(ProductionsiteSettings, ps, old_settings)) {
+			new_settings->stopped = ps->stopped;
+			for (const auto& pair_old : ps->ware_queues) {
+				for (auto& pair_new : new_settings->ware_queues) {
+					if (pair_new.first == pair_old.first) {
+						pair_new.second.priority = pair_old.second.priority;
+						pair_new.second.desired_fill = std::min(pair_old.second.desired_fill, pair_new.second.max_fill);
+						break;
+					}
+				}
+			}
+			for (const auto& pair_old : ps->worker_queues) {
+				for (auto& pair_new : new_settings->worker_queues) {
+					if (pair_new.first == pair_old.first) {
+						pair_new.second.priority = pair_old.second.priority;
+						pair_new.second.desired_fill = std::min(pair_old.second.desired_fill, pair_new.second.max_fill);
+						break;
+					}
+				}
+			}
+			if (upcast(TrainingsiteSettings, ts, old_settings)) {
+				new_settings->desired_capacity = std::min(new_settings->max_capacity, ts->desired_capacity);
+			}
+		}
+	} else if (upcast(const ProductionSiteDescr, pd, building_)) {
+		ProductionsiteSettings* new_settings = new ProductionsiteSettings(*pd);
+		settings_.reset(new_settings);
+		if (upcast(ProductionsiteSettings, ps, old_settings)) {
+			new_settings->stopped = ps->stopped;
+			for (const auto& pair_old : ps->ware_queues) {
+				for (auto& pair_new : new_settings->ware_queues) {
+					if (pair_new.first == pair_old.first) {
+						pair_new.second.priority = pair_old.second.priority;
+						pair_new.second.desired_fill = std::min(pair_old.second.desired_fill, pair_new.second.max_fill);
+						break;
+					}
+				}
+			}
+			for (const auto& pair_old : ps->worker_queues) {
+				for (auto& pair_new : new_settings->worker_queues) {
+					if (pair_new.first == pair_old.first) {
+						pair_new.second.priority = pair_old.second.priority;
+						pair_new.second.desired_fill = std::min(pair_old.second.desired_fill, pair_new.second.max_fill);
+						break;
+					}
+				}
+			}
+		}
+	} else if (upcast(const MilitarySiteDescr, md, building_)) {
+		MilitarysiteSettings* new_settings = new MilitarysiteSettings(*md);
+		settings_.reset(new_settings);
+		if (upcast(MilitarysiteSettings, ms, old_settings)) {
+			new_settings->desired_capacity = std::max<uint32_t>(1, std::min<uint32_t>(
+					new_settings->max_capacity, ms->desired_capacity));
+			new_settings->prefer_heroes = ms->prefer_heroes;
+		}
+	} else {
+		// TODO(Nordfriese): Add support for markets when trading is implemented
+		log("WARNING: Enhanced constructionsite to a %s, which is not of any known building type\n",
+				building_->name().c_str());
+	}
+	Notifications::publish(NoteImmovable(this, NoteImmovable::Ownership::GAINED));
+	Notifications::publish(NoteBuilding(serial(), NoteBuilding::Action::kChanged));
+}
+
+/*
+===============
 Construction sites only burn if some of the work has been completed.
 ===============
 */
@@ -345,6 +575,16 @@
 
 /*
 ===============
+Overwrite as many values of the current settings with those of the given settings as possible.
+===============
+*/
+void ConstructionSite::apply_settings(const BuildingSettings& cs) {
+	assert(settings_);
+	settings_->apply(cs);
+}
+
+/*
+===============
 Draw the construction site.
 ===============
 */

=== modified file 'src/logic/map_objects/tribes/constructionsite.h'
--- src/logic/map_objects/tribes/constructionsite.h	2019-05-05 14:05:07 +0000
+++ src/logic/map_objects/tribes/constructionsite.h	2019-05-14 17:38:23 +0000
@@ -20,16 +20,26 @@
 #ifndef WL_LOGIC_MAP_OBJECTS_TRIBES_CONSTRUCTIONSITE_H
 #define WL_LOGIC_MAP_OBJECTS_TRIBES_CONSTRUCTIONSITE_H
 
+#include <memory>
 #include <vector>
 
 #include "base/macros.h"
+#include "logic/map_objects/tribes/building_settings.h"
 #include "logic/map_objects/tribes/partially_finished_building.h"
 #include "scripting/lua_table.h"
 
+class FileRead;
+class FileWrite;
+
 namespace Widelands {
 
 class Building;
+class MilitarySiteDescr;
+class ProductionSiteDescr;
 class Request;
+enum class StockPolicy;
+class TrainingSiteDescr;
+class WarehouseDescr;
 class WaresQueue;
 
 /// Per-player and per-field constructionsite information
@@ -47,6 +57,7 @@
 	const BuildingDescr*
 	   becomes;  // Also works as a marker telling whether there is a construction site.
 	const BuildingDescr* was;  // only valid if "becomes" is an enhanced building.
+	std::vector<const BuildingDescr*> intermediates; // If we enhance a building while it's still under construction
 	uint32_t totaltime;
 	uint32_t completedtime;
 };
@@ -114,6 +125,13 @@
 	bool fetch_from_flag(Game&) override;
 	bool get_building_work(Game&, Worker&, bool success) override;
 
+	BuildingSettings* get_settings() const {
+		return settings_.get();
+	}
+	void apply_settings(const BuildingSettings&);
+
+	void enhance(Game&);
+
 protected:
 	void update_statistics_string(std::string* statistics_string) override;
 
@@ -135,6 +153,8 @@
 
 	bool builder_idle_;                 // used to determine whether the builder is idle
 	ConstructionsiteInformation info_;  // asked for by player point of view for the gameview
+
+	std::unique_ptr<BuildingSettings> settings_;
 };
 }  // namespace Widelands
 

=== modified file 'src/logic/map_objects/tribes/militarysite.cc'
--- src/logic/map_objects/tribes/militarysite.cc	2019-05-11 10:06:28 +0000
+++ src/logic/map_objects/tribes/militarysite.cc	2019-05-14 17:38:23 +0000
@@ -974,6 +974,13 @@
 	return false;
 }
 
+const BuildingSettings* MilitarySite::create_building_settings() const {
+	MilitarysiteSettings* settings = new MilitarysiteSettings(descr());
+	settings->desired_capacity = std::min(settings->max_capacity, soldier_control_.soldier_capacity());
+	settings->prefer_heroes = soldier_preference_ == SoldierPreference::kHeroes;
+	return settings;
+}
+
 // setters
 
 void MilitarySite::set_soldier_preference(SoldierPreference p) {

=== modified file 'src/logic/map_objects/tribes/militarysite.h'
--- src/logic/map_objects/tribes/militarysite.h	2019-05-05 14:05:07 +0000
+++ src/logic/map_objects/tribes/militarysite.h	2019-05-14 17:38:23 +0000
@@ -108,6 +108,8 @@
 		return soldier_preference_;
 	}
 
+	const BuildingSettings* create_building_settings() const override;
+
 protected:
 	void conquer_area(EditorGameBase&);
 

=== modified file 'src/logic/map_objects/tribes/productionsite.cc'
--- src/logic/map_objects/tribes/productionsite.cc	2019-05-12 09:58:40 +0000
+++ src/logic/map_objects/tribes/productionsite.cc	2019-05-14 17:38:23 +0000
@@ -1017,6 +1017,30 @@
 	set_production_result("");
 }
 
+const BuildingSettings* ProductionSite::create_building_settings() const {
+	ProductionsiteSettings* settings = new ProductionsiteSettings(descr());
+	settings->stopped = is_stopped_;
+	for (auto& pair : settings->ware_queues) {
+		pair.second.priority = get_priority(wwWARE, pair.first, false);
+		for (const auto& queue : input_queues_) {
+			if (queue->get_type() == wwWARE && queue->get_index() == pair.first) {
+				pair.second.desired_fill = std::min(pair.second.max_fill, queue->get_max_fill());
+				break;
+			}
+		}
+	}
+	for (auto& pair : settings->worker_queues) {
+		pair.second.priority = get_priority(wwWORKER, pair.first, false);
+		for (const auto& queue : input_queues_) {
+			if (queue->get_type() == wwWORKER && queue->get_index() == pair.first) {
+				pair.second.desired_fill = std::min(pair.second.max_fill, queue->get_max_fill());
+				break;
+			}
+		}
+	}
+	return settings;
+}
+
 /// Changes the default anim string to \li anim
 void ProductionSite::set_default_anim(std::string anim) {
 	if (default_anim_ == anim)

=== modified file 'src/logic/map_objects/tribes/productionsite.h'
--- src/logic/map_objects/tribes/productionsite.h	2019-05-11 12:37:45 +0000
+++ src/logic/map_objects/tribes/productionsite.h	2019-05-14 17:38:23 +0000
@@ -236,6 +236,8 @@
 
 	void set_default_anim(std::string);
 
+	const BuildingSettings* create_building_settings() const override;
+
 protected:
 	void update_statistics_string(std::string* statistics) override;
 

=== modified file 'src/logic/map_objects/tribes/trainingsite.cc'
--- src/logic/map_objects/tribes/trainingsite.cc	2019-05-05 14:05:07 +0000
+++ src/logic/map_objects/tribes/trainingsite.cc	2019-05-14 17:38:23 +0000
@@ -525,6 +525,31 @@
 	}
 }
 
+const BuildingSettings* TrainingSite::create_building_settings() const {
+	TrainingsiteSettings* settings = new TrainingsiteSettings(descr());
+	settings->desired_capacity = std::min(settings->max_capacity, soldier_control_.soldier_capacity());
+	settings->stopped = is_stopped_;
+	for (auto& pair : settings->ware_queues) {
+		pair.second.priority = get_priority(wwWARE, pair.first, false);
+		for (const auto& queue : input_queues_) {
+			if (queue->get_type() == wwWARE && queue->get_index() == pair.first) {
+				pair.second.desired_fill = std::min(pair.second.max_fill, queue->get_max_fill());
+				break;
+			}
+		}
+	}
+	for (auto& pair : settings->worker_queues) {
+		pair.second.priority = get_priority(wwWORKER, pair.first, false);
+		for (const auto& queue : input_queues_) {
+			if (queue->get_type() == wwWORKER && queue->get_index() == pair.first) {
+				pair.second.desired_fill = std::min(pair.second.max_fill, queue->get_max_fill());
+				break;
+			}
+		}
+	}
+	return settings;
+}
+
 /**
  * In addition to advancing the program, update soldier status.
  */

=== modified file 'src/logic/map_objects/tribes/trainingsite.h'
--- src/logic/map_objects/tribes/trainingsite.h	2019-05-05 14:05:07 +0000
+++ src/logic/map_objects/tribes/trainingsite.h	2019-05-14 17:38:23 +0000
@@ -199,6 +199,8 @@
 	void training_successful(TrainingAttribute type, uint32_t level);
 	void training_done();
 
+	const BuildingSettings* create_building_settings() const override;
+
 protected:
 	void program_end(Game&, ProgramResult) override;
 

=== modified file 'src/logic/map_objects/tribes/warehouse.cc'
--- src/logic/map_objects/tribes/warehouse.cc	2019-03-01 04:19:53 +0000
+++ src/logic/map_objects/tribes/warehouse.cc	2019-05-14 17:38:23 +0000
@@ -1275,17 +1275,17 @@
 	}
 }
 
-Warehouse::StockPolicy Warehouse::get_ware_policy(DescriptionIndex ware) const {
+StockPolicy Warehouse::get_ware_policy(DescriptionIndex ware) const {
 	assert(ware < static_cast<DescriptionIndex>(ware_policy_.size()));
 	return ware_policy_[ware];
 }
 
-Warehouse::StockPolicy Warehouse::get_worker_policy(DescriptionIndex ware) const {
+StockPolicy Warehouse::get_worker_policy(DescriptionIndex ware) const {
 	assert(ware < static_cast<DescriptionIndex>(worker_policy_.size()));
 	return worker_policy_[ware];
 }
 
-Warehouse::StockPolicy Warehouse::get_stock_policy(WareWorker waretype,
+StockPolicy Warehouse::get_stock_policy(WareWorker waretype,
                                                    DescriptionIndex wareindex) const {
 	if (waretype == wwWORKER)
 		return get_worker_policy(wareindex);
@@ -1293,25 +1293,25 @@
 		return get_ware_policy(wareindex);
 }
 
-void Warehouse::set_ware_policy(DescriptionIndex ware, Warehouse::StockPolicy policy) {
+void Warehouse::set_ware_policy(DescriptionIndex ware, StockPolicy policy) {
 	assert(ware < static_cast<DescriptionIndex>(ware_policy_.size()));
 	ware_policy_[ware] = policy;
 }
 
-void Warehouse::set_worker_policy(DescriptionIndex ware, Warehouse::StockPolicy policy) {
+void Warehouse::set_worker_policy(DescriptionIndex ware, StockPolicy policy) {
 	assert(ware < static_cast<DescriptionIndex>(worker_policy_.size()));
 	worker_policy_[ware] = policy;
 }
 
 /**
- * Check if there are remaining wares with \ref Warehouse::StockPolicy::kRemove,
+ * Check if there are remaining wares with \ref StockPolicy::kRemove,
  * and remove one of them if appropriate.
  */
 void Warehouse::check_remove_stock(Game& game) {
 	if (base_flag().current_wares() < base_flag().total_capacity() / 2) {
 		for (DescriptionIndex ware = 0; ware < static_cast<DescriptionIndex>(ware_policy_.size());
 		     ++ware) {
-			if (get_ware_policy(ware) != Warehouse::StockPolicy::kRemove || !get_wares().stock(ware))
+			if (get_ware_policy(ware) != StockPolicy::kRemove || !get_wares().stock(ware))
 				continue;
 
 			launch_ware(game, ware);
@@ -1321,7 +1321,7 @@
 
 	for (DescriptionIndex widx = 0; widx < static_cast<DescriptionIndex>(worker_policy_.size());
 	     ++widx) {
-		if (get_worker_policy(widx) != Warehouse::StockPolicy::kRemove || !get_workers().stock(widx))
+		if (get_worker_policy(widx) != StockPolicy::kRemove || !get_workers().stock(widx))
 			continue;
 
 		Worker& worker = launch_worker(game, widx, Requirements());
@@ -1337,6 +1337,18 @@
 	return portdock_->expedition_bootstrap()->inputqueue(index, type);
 }
 
+const BuildingSettings* Warehouse::create_building_settings() const {
+	WarehouseSettings* settings = new WarehouseSettings(descr(), owner().tribe());
+	for (auto& pair : settings->ware_preferences) {
+		pair.second = get_ware_policy(pair.first);
+	}
+	for (auto& pair : settings->worker_preferences) {
+		pair.second = get_worker_policy(pair.first);
+	}
+	settings->launch_expedition = portdock_ && portdock_->expedition_started();
+	return settings;
+}
+
 void Warehouse::log_general_info(const EditorGameBase& egbase) const {
 	Building::log_general_info(egbase);
 

=== modified file 'src/logic/map_objects/tribes/warehouse.h'
--- src/logic/map_objects/tribes/warehouse.h	2019-05-05 14:05:07 +0000
+++ src/logic/map_objects/tribes/warehouse.h	2019-05-14 17:38:23 +0000
@@ -68,6 +68,40 @@
 	DISALLOW_COPY_AND_ASSIGN(WarehouseDescr);
 };
 
+/**
+ * Each ware and worker type has an associated per-warehouse
+ * stock policy that defines whether it will be stocked by this
+ * warehouse.
+ *
+ * \note The values of this enum are written directly into savegames,
+ * so be careful when changing them.
+ */
+enum class StockPolicy {
+	/**
+	 * The default policy allows stocking wares without any special priority.
+	 */
+	kNormal = 0,
+
+	/**
+	 * As long as there are warehouses with this policy for a ware, all
+	 * available unstocked supplies will be transferred to warehouses
+	 * with this policy.
+	 */
+	kPrefer = 1,
+
+	/**
+	 * If a ware has this stock policy, no more of this ware will enter
+	 * the warehouse.
+	 */
+	kDontStock = 2,
+
+	/**
+	 * Like \ref kDontStock, but in addition, existing stock of this ware
+	 * will be transported out of the warehouse over time.
+	 */
+	kRemove = 3,
+};
+
 class Warehouse : public Building {
 	friend class PortDock;
 	friend class MapBuildingdataPacket;
@@ -75,39 +109,6 @@
 	MO_DESCR(WarehouseDescr)
 
 public:
-	/**
-	 * Each ware and worker type has an associated per-warehouse
-	 * stock policy that defines whether it will be stocked by this
-	 * warehouse.
-	 *
-	 * \note The values of this enum are written directly into savegames,
-	 * so be careful when changing them.
-	 */
-	enum class StockPolicy {
-		/**
-		 * The default policy allows stocking wares without any special priority.
-		 */
-		kNormal = 0,
-
-		/**
-		 * As long as there are warehouses with this policy for a ware, all
-		 * available unstocked supplies will be transferred to warehouses
-		 * with this policy.
-		 */
-		kPrefer = 1,
-
-		/**
-		 * If a ware has this stock policy, no more of this ware will enter
-		 * the warehouse.
-		 */
-		kDontStock = 2,
-
-		/**
-		 * Like \ref kDontStock, but in addition, existing stock of this ware
-		 * will be transported out of the warehouse over time.
-		 */
-		kRemove = 3,
-	};
 
 	/**
 	 * Whether worker indices in count_workers() have to match exactly.
@@ -208,6 +209,8 @@
 		return portdock_;
 	}
 
+	const BuildingSettings* create_building_settings() const override;
+
 	// Returns the waresqueue of the expedition if this is a port.
 	// Will throw an exception otherwise.
 	InputQueue& inputqueue(DescriptionIndex, WareWorker) override;

=== modified file 'src/logic/player.cc'
--- src/logic/player.cc	2019-05-11 18:19:20 +0000
+++ src/logic/player.cc	2019-05-14 17:38:23 +0000
@@ -760,6 +760,7 @@
 		} else {
 			workers = building->get_workers();
 		}
+		const BuildingSettings* settings = building->create_building_settings();
 
 		if (index_of_new_building != INVALID_INDEX) {
 			// For enhancing, register whether the window was open
@@ -770,7 +771,7 @@
 		//  pointer.
 		if (index_of_new_building != INVALID_INDEX)
 			building = &egbase().warp_constructionsite(
-			   position, player_number_, index_of_new_building, false, former_buildings);
+			   position, player_number_, index_of_new_building, false, former_buildings, settings);
 		else
 			building = &egbase().warp_dismantlesite(position, player_number_, false, former_buildings);
 

=== modified file 'src/logic/playercommand.cc'
--- src/logic/playercommand.cc	2019-05-07 12:26:11 +0000
+++ src/logic/playercommand.cc	2019-05-14 17:38:23 +0000
@@ -108,6 +108,14 @@
 	PLCMD_SHIP_SINK = 29,
 	PLCMD_SHIP_CANCELEXPEDITION = 30,
 	PLCMD_PROPOSE_TRADE = 31,
+	PLCMD_CONSTRUCTIONSITE_SOLDIERCAPACITY = 32,
+	PLCMD_CONSTRUCTIONSITE_PREFERHEROES = 33,
+	PLCMD_CONSTRUCTIONSITE_INPUTQUEUE_PRIORITY = 34,
+	PLCMD_CONSTRUCTIONSITE_INPUTQUEUE_MAXFILL = 35,
+	PLCMD_CONSTRUCTIONSITE_STOCKPOLICY = 36,
+	PLCMD_CONSTRUCTIONSITE_LAUNCHEXPEDITION = 37,
+	PLCMD_CONSTRUCTIONSITE_ENHANCE = 38,
+	PLCMD_CONSTRUCTIONSITE_STARTSTOP = 39,
 };
 
 /*** class PlayerCommand ***/
@@ -176,6 +184,22 @@
 		return new CmdEvictWorker(des);
 	case PLCMD_MILITARYSITESETSOLDIERPREFERENCE:
 		return new CmdMilitarySiteSetSoldierPreference(des);
+	case PLCMD_CONSTRUCTIONSITE_SOLDIERCAPACITY:
+		return new CmdConstructionsiteSoldierCapacity(des);
+	case PLCMD_CONSTRUCTIONSITE_PREFERHEROES:
+		return new CmdConstructionsitePreferHeroes(des);
+	case PLCMD_CONSTRUCTIONSITE_INPUTQUEUE_PRIORITY:
+		return new CmdConstructionsiteInputQueuePriority(des);
+	case PLCMD_CONSTRUCTIONSITE_INPUTQUEUE_MAXFILL:
+		return new CmdConstructionsiteInputQueueMaxFill(des);
+	case PLCMD_CONSTRUCTIONSITE_STOCKPOLICY:
+		return new CmdConstructionsiteStockPolicy(des);
+	case PLCMD_CONSTRUCTIONSITE_LAUNCHEXPEDITION:
+		return new CmdConstructionsiteLaunchExpedition(des);
+	case PLCMD_CONSTRUCTIONSITE_ENHANCE:
+		return new CmdConstructionsiteEnhance(des);
+	case PLCMD_CONSTRUCTIONSITE_STARTSTOP:
+		return new CmdConstructionsiteStartStop(des);
 	default:
 		throw wexception("PlayerCommand::deserialize(): Invalid command id encountered");
 	}
@@ -1751,7 +1775,7 @@
                                      Warehouse& wh,
                                      bool isworker,
                                      DescriptionIndex ware,
-                                     Warehouse::StockPolicy policy)
+                                     StockPolicy policy)
    : PlayerCommand(time, p) {
 	warehouse_ = wh.serial();
 	isworker_ = isworker;
@@ -1794,7 +1818,7 @@
 	warehouse_ = des.unsigned_32();
 	isworker_ = des.unsigned_8();
 	ware_ = DescriptionIndex(des.unsigned_8());
-	policy_ = static_cast<Warehouse::StockPolicy>(des.unsigned_8());
+	policy_ = static_cast<StockPolicy>(des.unsigned_8());
 }
 
 void CmdSetStockPolicy::serialize(StreamWrite& ser) {
@@ -1816,7 +1840,7 @@
 			warehouse_ = fr.unsigned_32();
 			isworker_ = fr.unsigned_8();
 			ware_ = DescriptionIndex(fr.unsigned_8());
-			policy_ = static_cast<Warehouse::StockPolicy>(fr.unsigned_8());
+			policy_ = static_cast<StockPolicy>(fr.unsigned_8());
 		} else {
 			throw UnhandledVersionError(
 			   "CmdSetStockPolicy", packet_version, kCurrentPacketVersionCmdSetStockPolicy);
@@ -1904,4 +1928,601 @@
 	// TODO(sirver,trading): Implement this.
 	NEVER_HERE();
 }
+
+
+/*** struct CmdConstructionsiteSoldierCapacity ***/
+CmdConstructionsiteSoldierCapacity::CmdConstructionsiteSoldierCapacity(uint32_t time,
+                                     PlayerNumber p,
+                                     ConstructionSite& cs,
+                                     uint32_t cap)
+   : PlayerCommand(time, p), constructionsite_(cs.serial()), capacity_(cap) {
+}
+
+CmdConstructionsiteSoldierCapacity::CmdConstructionsiteSoldierCapacity()
+   : PlayerCommand(), constructionsite_(0), capacity_(0) {
+}
+
+void CmdConstructionsiteSoldierCapacity::execute(Game& game) {
+	if (Player* plr = game.get_player(sender())) {
+		if (upcast(ConstructionSite, cs, game.objects().get_object(constructionsite_))) {
+			if (cs->get_owner() != plr) {
+				log("CmdConstructionsiteSoldierCapacity: sender %u, but site owner %u\n", sender(),
+				    cs->owner().player_number());
+				return;
+			}
+			if (upcast(MilitarysiteSettings, ms, cs->get_settings())) {
+				assert(ms->max_capacity >= capacity_);
+				ms->desired_capacity = capacity_;
+			} else if (upcast(TrainingsiteSettings, ts, cs->get_settings())) {
+				assert(ts->max_capacity >= capacity_);
+				ts->desired_capacity = capacity_;
+			}
+		}
+	}
+}
+
+CmdConstructionsiteSoldierCapacity::CmdConstructionsiteSoldierCapacity(StreamRead& des)
+		: PlayerCommand(0, des.unsigned_8()) {
+	constructionsite_ = des.unsigned_32();
+	capacity_ = des.unsigned_32();
+}
+
+void CmdConstructionsiteSoldierCapacity::serialize(StreamWrite& ser) {
+	ser.unsigned_8(PLCMD_CONSTRUCTIONSITE_SOLDIERCAPACITY);
+	ser.unsigned_8(sender());
+	ser.unsigned_32(constructionsite_);
+	ser.unsigned_32(capacity_);
+}
+
+constexpr uint8_t kCurrentPacketVersionCmdConstructionsiteSoldierCapacity = 1;
+
+void CmdConstructionsiteSoldierCapacity::read(FileRead& fr, EditorGameBase& egbase, MapObjectLoader& mol) {
+	try {
+		uint8_t packet_version = fr.unsigned_8();
+		if (packet_version == kCurrentPacketVersionCmdConstructionsiteSoldierCapacity) {
+			PlayerCommand::read(fr, egbase, mol);
+			constructionsite_ = fr.unsigned_32();
+			capacity_ = fr.unsigned_32();
+		} else {
+			throw UnhandledVersionError("CmdConstructionsiteSoldierCapacity", packet_version,
+					kCurrentPacketVersionCmdConstructionsiteSoldierCapacity);
+		}
+	} catch (const std::exception& e) {
+		throw GameDataError("CmdConstructionsiteSoldierCapacity: %s", e.what());
+	}
+}
+
+void CmdConstructionsiteSoldierCapacity::write(FileWrite& fw, EditorGameBase& egbase, MapObjectSaver& mos) {
+	fw.unsigned_8(kCurrentPacketVersionCmdConstructionsiteSoldierCapacity);
+	PlayerCommand::write(fw, egbase, mos);
+	fw.unsigned_32(constructionsite_);
+	fw.unsigned_32(capacity_);
+}
+
+/*** struct CmdConstructionsitePreferHeroes ***/
+CmdConstructionsitePreferHeroes::CmdConstructionsitePreferHeroes(uint32_t time,
+                                     PlayerNumber p,
+                                     ConstructionSite& cs,
+                                     bool h)
+   : PlayerCommand(time, p), constructionsite_(cs.serial()), heroes_(h) {
+}
+
+CmdConstructionsitePreferHeroes::CmdConstructionsitePreferHeroes()
+   : PlayerCommand(), constructionsite_(0), heroes_(false) {
+}
+
+void CmdConstructionsitePreferHeroes::execute(Game& game) {
+	if (Player* plr = game.get_player(sender())) {
+		if (upcast(ConstructionSite, cs, game.objects().get_object(constructionsite_))) {
+			if (cs->get_owner() != plr) {
+				log("CmdConstructionsitePreferHeroes: sender %u, but site owner %u\n", sender(),
+				    cs->owner().player_number());
+				return;
+			}
+			if (upcast(MilitarysiteSettings, s, cs->get_settings())) {
+				s->prefer_heroes = heroes_;
+			}
+		}
+	}
+}
+
+CmdConstructionsitePreferHeroes::CmdConstructionsitePreferHeroes(StreamRead& des)
+		: PlayerCommand(0, des.unsigned_8()) {
+	constructionsite_ = des.unsigned_32();
+	heroes_ = des.unsigned_8();
+}
+
+void CmdConstructionsitePreferHeroes::serialize(StreamWrite& ser) {
+	ser.unsigned_8(PLCMD_CONSTRUCTIONSITE_PREFERHEROES);
+	ser.unsigned_8(sender());
+	ser.unsigned_32(constructionsite_);
+	ser.unsigned_8(heroes_ ? 1 : 0);
+}
+
+constexpr uint8_t kCurrentPacketVersionCmdConstructionsitePreferHeroes = 1;
+
+void CmdConstructionsitePreferHeroes::read(FileRead& fr, EditorGameBase& egbase, MapObjectLoader& mol) {
+	try {
+		uint8_t packet_version = fr.unsigned_8();
+		if (packet_version == kCurrentPacketVersionCmdConstructionsitePreferHeroes) {
+			PlayerCommand::read(fr, egbase, mol);
+			constructionsite_ = fr.unsigned_32();
+			heroes_ = fr.unsigned_8();
+		} else {
+			throw UnhandledVersionError("CmdConstructionsitePreferHeroes", packet_version,
+					kCurrentPacketVersionCmdConstructionsitePreferHeroes);
+		}
+	} catch (const std::exception& e) {
+		throw GameDataError("CmdConstructionsitePreferHeroes: %s", e.what());
+	}
+}
+
+void CmdConstructionsitePreferHeroes::write(FileWrite& fw, EditorGameBase& egbase, MapObjectSaver& mos) {
+	fw.unsigned_8(kCurrentPacketVersionCmdConstructionsitePreferHeroes);
+	PlayerCommand::write(fw, egbase, mos);
+	fw.unsigned_32(constructionsite_);
+	fw.unsigned_8(heroes_ ? 1 : 0);
+}
+
+/*** struct CmdConstructionsiteLaunchExpedition ***/
+CmdConstructionsiteLaunchExpedition::CmdConstructionsiteLaunchExpedition(uint32_t time,
+                                     PlayerNumber p,
+                                     ConstructionSite& cs,
+                                     bool l)
+   : PlayerCommand(time, p), constructionsite_(cs.serial()), launch_(l) {
+}
+
+CmdConstructionsiteLaunchExpedition::CmdConstructionsiteLaunchExpedition()
+   : PlayerCommand(), constructionsite_(0), launch_(false) {
+}
+
+void CmdConstructionsiteLaunchExpedition::execute(Game& game) {
+	if (Player* plr = game.get_player(sender())) {
+		if (upcast(ConstructionSite, cs, game.objects().get_object(constructionsite_))) {
+			if (cs->get_owner() != plr) {
+				log("CmdConstructionsiteLaunchExpedition: sender %u, but site owner %u\n", sender(),
+				    cs->owner().player_number());
+				return;
+			}
+			if (upcast(WarehouseSettings, s, cs->get_settings())) {
+				s->launch_expedition = launch_;
+			}
+		}
+	}
+}
+
+CmdConstructionsiteLaunchExpedition::CmdConstructionsiteLaunchExpedition(StreamRead& des)
+		: PlayerCommand(0, des.unsigned_8()) {
+	constructionsite_ = des.unsigned_32();
+	launch_ = des.unsigned_8();
+}
+
+void CmdConstructionsiteLaunchExpedition::serialize(StreamWrite& ser) {
+	ser.unsigned_8(PLCMD_CONSTRUCTIONSITE_LAUNCHEXPEDITION);
+	ser.unsigned_8(sender());
+	ser.unsigned_32(constructionsite_);
+	ser.unsigned_8(launch_ ? 1 : 0);
+}
+
+constexpr uint8_t kCurrentPacketVersionCmdConstructionsiteLaunchExpedition = 1;
+
+void CmdConstructionsiteLaunchExpedition::read(FileRead& fr, EditorGameBase& egbase, MapObjectLoader& mol) {
+	try {
+		uint8_t packet_version = fr.unsigned_8();
+		if (packet_version == kCurrentPacketVersionCmdConstructionsiteLaunchExpedition) {
+			PlayerCommand::read(fr, egbase, mol);
+			constructionsite_ = fr.unsigned_32();
+			launch_ = fr.unsigned_8();
+		} else {
+			throw UnhandledVersionError("CmdConstructionsiteLaunchExpedition", packet_version,
+					kCurrentPacketVersionCmdConstructionsiteLaunchExpedition);
+		}
+	} catch (const std::exception& e) {
+		throw GameDataError("CmdConstructionsiteLaunchExpedition: %s", e.what());
+	}
+}
+
+void CmdConstructionsiteLaunchExpedition::write(FileWrite& fw, EditorGameBase& egbase, MapObjectSaver& mos) {
+	fw.unsigned_8(kCurrentPacketVersionCmdConstructionsiteLaunchExpedition);
+	PlayerCommand::write(fw, egbase, mos);
+	fw.unsigned_32(constructionsite_);
+	fw.unsigned_8(launch_ ? 1 : 0);
+}
+
+/*** struct CmdConstructionsiteStartStop ***/
+CmdConstructionsiteStartStop::CmdConstructionsiteStartStop(uint32_t time,
+                                     PlayerNumber p,
+                                     ConstructionSite& cs,
+                                     bool stop)
+   : PlayerCommand(time, p), constructionsite_(cs.serial()), stopped_(stop) {
+}
+
+CmdConstructionsiteStartStop::CmdConstructionsiteStartStop()
+   : PlayerCommand(), constructionsite_(0), stopped_(false) {
+}
+
+void CmdConstructionsiteStartStop::execute(Game& game) {
+	if (Player* plr = game.get_player(sender())) {
+		if (upcast(ConstructionSite, cs, game.objects().get_object(constructionsite_))) {
+			if (cs->get_owner() != plr) {
+				log("CmdConstructionsiteStartStop: sender %u, but site owner %u\n", sender(),
+				    cs->owner().player_number());
+				return;
+			}
+			if (upcast(ProductionsiteSettings, s, cs->get_settings())) {
+				s->stopped = stopped_;
+			}
+		}
+	}
+}
+
+CmdConstructionsiteStartStop::CmdConstructionsiteStartStop(StreamRead& des)
+		: PlayerCommand(0, des.unsigned_8()) {
+	constructionsite_ = des.unsigned_32();
+	stopped_ = des.unsigned_8();
+}
+
+void CmdConstructionsiteStartStop::serialize(StreamWrite& ser) {
+	ser.unsigned_8(PLCMD_CONSTRUCTIONSITE_STARTSTOP);
+	ser.unsigned_8(sender());
+	ser.unsigned_32(constructionsite_);
+	ser.unsigned_8(stopped_ ? 1 : 0);
+}
+
+constexpr uint8_t kCurrentPacketVersionCmdConstructionsiteStartStop = 1;
+
+void CmdConstructionsiteStartStop::read(FileRead& fr, EditorGameBase& egbase, MapObjectLoader& mol) {
+	try {
+		uint8_t packet_version = fr.unsigned_8();
+		if (packet_version == kCurrentPacketVersionCmdConstructionsiteStartStop) {
+			PlayerCommand::read(fr, egbase, mol);
+			constructionsite_ = fr.unsigned_32();
+			stopped_ = fr.unsigned_8();
+		} else {
+			throw UnhandledVersionError("CmdConstructionsiteStartStop", packet_version,
+					kCurrentPacketVersionCmdConstructionsiteStartStop);
+		}
+	} catch (const std::exception& e) {
+		throw GameDataError("CmdConstructionsiteStartStop: %s", e.what());
+	}
+}
+
+void CmdConstructionsiteStartStop::write(FileWrite& fw, EditorGameBase& egbase, MapObjectSaver& mos) {
+	fw.unsigned_8(kCurrentPacketVersionCmdConstructionsiteStartStop);
+	PlayerCommand::write(fw, egbase, mos);
+	fw.unsigned_32(constructionsite_);
+	fw.unsigned_8(stopped_ ? 1 : 0);
+}
+
+/*** struct CmdConstructionsiteStockPolicy ***/
+CmdConstructionsiteStockPolicy::CmdConstructionsiteStockPolicy(uint32_t time,
+                                     PlayerNumber p,
+                                     ConstructionSite& cs,
+                                     WareWorker ww,
+                                     DescriptionIndex di,
+                                     StockPolicy pol)
+   : PlayerCommand(time, p), constructionsite_(cs.serial()), wwtype_(ww), index_(di), policy_(pol) {
+}
+
+CmdConstructionsiteStockPolicy::CmdConstructionsiteStockPolicy()
+   : PlayerCommand(), constructionsite_(0), wwtype_(wwWARE), index_(INVALID_INDEX), policy_() {
+}
+
+void CmdConstructionsiteStockPolicy::execute(Game& game) {
+	if (Player* plr = game.get_player(sender())) {
+		if (upcast(ConstructionSite, cs, game.objects().get_object(constructionsite_))) {
+			if (cs->get_owner() != plr) {
+				log("CmdConstructionsiteStockPolicy: sender %u, but site owner %u\n", sender(),
+				    cs->owner().player_number());
+				return;
+			}
+			if (upcast(WarehouseSettings, s, cs->get_settings())) {
+				switch (wwtype_) {
+					case wwWARE:
+						s->ware_preferences[index_] = policy_;
+						break;
+					case wwWORKER:
+						s->worker_preferences[index_] = policy_;
+						break;
+					NEVER_HERE();
+				}
+			}
+		}
+	}
+}
+
+CmdConstructionsiteStockPolicy::CmdConstructionsiteStockPolicy(StreamRead& des)
+		: PlayerCommand(0, des.unsigned_8()) {
+	constructionsite_ = des.unsigned_32();
+	wwtype_ = des.unsigned_8() == 0 ? wwWARE : wwWORKER;
+	index_ = des.unsigned_32();
+	policy_ = static_cast<StockPolicy>(des.unsigned_8());
+}
+
+void CmdConstructionsiteStockPolicy::serialize(StreamWrite& ser) {
+	ser.unsigned_8(PLCMD_CONSTRUCTIONSITE_STOCKPOLICY);
+	ser.unsigned_8(sender());
+	ser.unsigned_32(constructionsite_);
+	ser.unsigned_8(wwtype_ == wwWARE ? 0 : 1);
+	ser.unsigned_32(index_);
+	ser.unsigned_8(static_cast<uint8_t>(policy_));
+}
+
+constexpr uint8_t kCurrentPacketVersionCmdConstructionsiteStockPolicy = 1;
+
+void CmdConstructionsiteStockPolicy::read(FileRead& fr, EditorGameBase& egbase, MapObjectLoader& mol) {
+	try {
+		uint8_t packet_version = fr.unsigned_8();
+		if (packet_version == kCurrentPacketVersionCmdConstructionsiteStockPolicy) {
+			PlayerCommand::read(fr, egbase, mol);
+			constructionsite_ = fr.unsigned_32();
+			wwtype_ = fr.unsigned_8() == 0 ? wwWARE : wwWORKER;
+			index_ = fr.unsigned_32();
+			policy_ = static_cast<StockPolicy>(fr.unsigned_8());
+		} else {
+			throw UnhandledVersionError("CmdConstructionsiteStockPolicy", packet_version,
+					kCurrentPacketVersionCmdConstructionsiteStockPolicy);
+		}
+	} catch (const std::exception& e) {
+		throw GameDataError("CmdConstructionsiteStockPolicy: %s", e.what());
+	}
+}
+
+void CmdConstructionsiteStockPolicy::write(FileWrite& fw, EditorGameBase& egbase, MapObjectSaver& mos) {
+	fw.unsigned_8(kCurrentPacketVersionCmdConstructionsiteStockPolicy);
+	PlayerCommand::write(fw, egbase, mos);
+	fw.unsigned_32(constructionsite_);
+	fw.unsigned_8(wwtype_ == wwWARE ? 0 : 1);
+	fw.unsigned_32(index_);
+	fw.unsigned_8(static_cast<uint8_t>(policy_));
+}
+
+/*** struct CmdConstructionsiteInputQueuePriority ***/
+CmdConstructionsiteInputQueuePriority::CmdConstructionsiteInputQueuePriority(uint32_t time,
+                                     PlayerNumber p,
+                                     ConstructionSite& cs,
+                                     WareWorker ww,
+                                     DescriptionIndex di,
+                                     int32_t prio)
+   : PlayerCommand(time, p), constructionsite_(cs.serial()), wwtype_(ww), index_(di), priority_(prio) {
+}
+
+CmdConstructionsiteInputQueuePriority::CmdConstructionsiteInputQueuePriority()
+   : PlayerCommand(), constructionsite_(0), wwtype_(wwWARE), index_(INVALID_INDEX), priority_(0) {
+}
+
+void CmdConstructionsiteInputQueuePriority::execute(Game& game) {
+	if (Player* plr = game.get_player(sender())) {
+		if (upcast(ConstructionSite, cs, game.objects().get_object(constructionsite_))) {
+			if (cs->get_owner() != plr) {
+				log("CmdConstructionsiteInputQueuePriority: sender %u, but site owner %u\n", sender(),
+				    cs->owner().player_number());
+				return;
+			}
+			if (upcast(ProductionsiteSettings, s, cs->get_settings())) {
+				switch (wwtype_) {
+					case wwWARE:
+						for (auto& pair : s->ware_queues) {
+							if (pair.first == index_) {
+								pair.second.priority = priority_;
+								return;
+							}
+						}
+						NEVER_HERE();
+					case wwWORKER:
+						for (auto& pair : s->worker_queues) {
+							if (pair.first == index_) {
+								pair.second.priority = priority_;
+								return;
+							}
+						}
+						NEVER_HERE();
+					default:
+						NEVER_HERE();
+				}
+			}
+		}
+	}
+}
+
+CmdConstructionsiteInputQueuePriority::CmdConstructionsiteInputQueuePriority(StreamRead& des)
+		: PlayerCommand(0, des.unsigned_8()) {
+	constructionsite_ = des.unsigned_32();
+	wwtype_ = des.unsigned_8() == 0 ? wwWARE : wwWORKER;
+	index_ = des.unsigned_32();
+	priority_ = des.signed_32();
+}
+
+void CmdConstructionsiteInputQueuePriority::serialize(StreamWrite& ser) {
+	ser.unsigned_8(PLCMD_CONSTRUCTIONSITE_INPUTQUEUE_PRIORITY);
+	ser.unsigned_8(sender());
+	ser.unsigned_32(constructionsite_);
+	ser.unsigned_8(wwtype_ == wwWARE ? 0 : 1);
+	ser.unsigned_32(index_);
+	ser.signed_32(priority_);
+}
+
+constexpr uint8_t kCurrentPacketVersionCmdConstructionsiteInputQueuePriority = 1;
+
+void CmdConstructionsiteInputQueuePriority::read(FileRead& fr, EditorGameBase& egbase, MapObjectLoader& mol) {
+	try {
+		uint8_t packet_version = fr.unsigned_8();
+		if (packet_version == kCurrentPacketVersionCmdConstructionsiteInputQueuePriority) {
+			PlayerCommand::read(fr, egbase, mol);
+			constructionsite_ = fr.unsigned_32();
+			wwtype_ = fr.unsigned_8() == 0 ? wwWARE : wwWORKER;
+			index_ = fr.unsigned_32();
+			priority_ = fr.signed_32();
+		} else {
+			throw UnhandledVersionError("CmdConstructionsiteInputQueuePriority", packet_version,
+					kCurrentPacketVersionCmdConstructionsiteInputQueuePriority);
+		}
+	} catch (const std::exception& e) {
+		throw GameDataError("CmdConstructionsiteInputQueuePriority: %s", e.what());
+	}
+}
+
+void CmdConstructionsiteInputQueuePriority::write(FileWrite& fw, EditorGameBase& egbase, MapObjectSaver& mos) {
+	fw.unsigned_8(kCurrentPacketVersionCmdConstructionsiteInputQueuePriority);
+	PlayerCommand::write(fw, egbase, mos);
+	fw.unsigned_32(constructionsite_);
+	fw.unsigned_8(wwtype_ == wwWARE ? 0 : 1);
+	fw.unsigned_32(index_);
+	fw.signed_32(priority_);
+}
+
+/*** struct CmdConstructionsiteInputQueueMaxFill ***/
+CmdConstructionsiteInputQueueMaxFill::CmdConstructionsiteInputQueueMaxFill(uint32_t time,
+                                     PlayerNumber p,
+                                     ConstructionSite& cs,
+                                     WareWorker ww,
+                                     DescriptionIndex di,
+                                     uint32_t max)
+   : PlayerCommand(time, p), constructionsite_(cs.serial()), wwtype_(ww), index_(di), max_fill_(max) {
+}
+
+CmdConstructionsiteInputQueueMaxFill::CmdConstructionsiteInputQueueMaxFill()
+   : PlayerCommand(), constructionsite_(0), wwtype_(wwWARE), index_(INVALID_INDEX), max_fill_(0) {
+}
+
+void CmdConstructionsiteInputQueueMaxFill::execute(Game& game) {
+	if (Player* plr = game.get_player(sender())) {
+		if (upcast(ConstructionSite, cs, game.objects().get_object(constructionsite_))) {
+			if (cs->get_owner() != plr) {
+				log("CmdConstructionsiteInputQueueMaxFill: sender %u, but site owner %u\n", sender(),
+				    cs->owner().player_number());
+				return;
+			}
+			if (upcast(ProductionsiteSettings, s, cs->get_settings())) {
+				switch (wwtype_) {
+					case wwWARE:
+						for (auto& pair : s->ware_queues) {
+							if (pair.first == index_) {
+								assert(pair.second.max_fill >= max_fill_);
+								pair.second.desired_fill = max_fill_;
+								return;
+							}
+						}
+						NEVER_HERE();
+					case wwWORKER:
+						for (auto& pair : s->worker_queues) {
+							if (pair.first == index_) {
+								assert(pair.second.max_fill >= max_fill_);
+								pair.second.desired_fill = max_fill_;
+								return;
+							}
+						}
+						NEVER_HERE();
+					default:
+						NEVER_HERE();
+				}
+			}
+		}
+	}
+}
+
+CmdConstructionsiteInputQueueMaxFill::CmdConstructionsiteInputQueueMaxFill(StreamRead& des)
+		: PlayerCommand(0, des.unsigned_8()) {
+	constructionsite_ = des.unsigned_32();
+	wwtype_ = des.unsigned_8() == 0 ? wwWARE : wwWORKER;
+	index_ = des.unsigned_32();
+	max_fill_ = des.unsigned_32();
+}
+
+void CmdConstructionsiteInputQueueMaxFill::serialize(StreamWrite& ser) {
+	ser.unsigned_8(PLCMD_CONSTRUCTIONSITE_INPUTQUEUE_MAXFILL);
+	ser.unsigned_8(sender());
+	ser.unsigned_32(constructionsite_);
+	ser.unsigned_8(wwtype_ == wwWARE ? 0 : 1);
+	ser.unsigned_32(index_);
+	ser.unsigned_32(max_fill_);
+}
+
+constexpr uint8_t kCurrentPacketVersionCmdConstructionsiteInputQueueMaxFill = 1;
+
+void CmdConstructionsiteInputQueueMaxFill::read(FileRead& fr, EditorGameBase& egbase, MapObjectLoader& mol) {
+	try {
+		uint8_t packet_version = fr.unsigned_8();
+		if (packet_version == kCurrentPacketVersionCmdConstructionsiteInputQueueMaxFill) {
+			PlayerCommand::read(fr, egbase, mol);
+			constructionsite_ = fr.unsigned_32();
+			wwtype_ = fr.unsigned_8() == 0 ? wwWARE : wwWORKER;
+			index_ = fr.unsigned_32();
+			max_fill_ = fr.unsigned_32();
+		} else {
+			throw UnhandledVersionError("CmdConstructionsiteInputQueueMaxFill", packet_version,
+					kCurrentPacketVersionCmdConstructionsiteInputQueueMaxFill);
+		}
+	} catch (const std::exception& e) {
+		throw GameDataError("CmdConstructionsiteInputQueueMaxFill: %s", e.what());
+	}
+}
+
+void CmdConstructionsiteInputQueueMaxFill::write(FileWrite& fw, EditorGameBase& egbase, MapObjectSaver& mos) {
+	fw.unsigned_8(kCurrentPacketVersionCmdConstructionsiteInputQueueMaxFill);
+	PlayerCommand::write(fw, egbase, mos);
+	fw.unsigned_32(constructionsite_);
+	fw.unsigned_8(wwtype_ == wwWARE ? 0 : 1);
+	fw.unsigned_32(index_);
+	fw.unsigned_32(max_fill_);
+}
+
+/*** struct CmdConstructionsiteEnhance ***/
+CmdConstructionsiteEnhance::CmdConstructionsiteEnhance(uint32_t time,
+                                     PlayerNumber p,
+                                     ConstructionSite& cs)
+   : PlayerCommand(time, p), constructionsite_(cs.serial()) {
+}
+
+CmdConstructionsiteEnhance::CmdConstructionsiteEnhance()
+   : PlayerCommand(), constructionsite_(0) {
+}
+
+void CmdConstructionsiteEnhance::execute(Game& game) {
+	if (Player* plr = game.get_player(sender())) {
+		if (upcast(ConstructionSite, cs, game.objects().get_object(constructionsite_))) {
+			if (cs->get_owner() != plr) {
+				log("CmdConstructionsiteEnhance: sender %u, but site owner %u\n", sender(),
+				    cs->owner().player_number());
+				return;
+			}
+			cs->enhance(game);
+		}
+	}
+}
+
+CmdConstructionsiteEnhance::CmdConstructionsiteEnhance(StreamRead& des)
+		: PlayerCommand(0, des.unsigned_8()) {
+	constructionsite_ = des.unsigned_32();
+}
+
+void CmdConstructionsiteEnhance::serialize(StreamWrite& ser) {
+	ser.unsigned_8(PLCMD_CONSTRUCTIONSITE_ENHANCE);
+	ser.unsigned_8(sender());
+	ser.unsigned_32(constructionsite_);
+}
+
+constexpr uint8_t kCurrentPacketVersionCmdConstructionsiteEnhance = 1;
+
+void CmdConstructionsiteEnhance::read(FileRead& fr, EditorGameBase& egbase, MapObjectLoader& mol) {
+	try {
+		uint8_t packet_version = fr.unsigned_8();
+		if (packet_version == kCurrentPacketVersionCmdConstructionsiteEnhance) {
+			PlayerCommand::read(fr, egbase, mol);
+			constructionsite_ = fr.unsigned_32();
+		} else {
+			throw UnhandledVersionError("CmdConstructionsiteEnhance", packet_version,
+					kCurrentPacketVersionCmdConstructionsiteEnhance);
+		}
+	} catch (const std::exception& e) {
+		throw GameDataError("CmdConstructionsiteEnhance: %s", e.what());
+	}
+}
+
+void CmdConstructionsiteEnhance::write(FileWrite& fw, EditorGameBase& egbase, MapObjectSaver& mos) {
+	fw.unsigned_8(kCurrentPacketVersionCmdConstructionsiteEnhance);
+	PlayerCommand::write(fw, egbase, mos);
+	fw.unsigned_32(constructionsite_);
+}
+
 }  // namespace Widelands

=== modified file 'src/logic/playercommand.h'
--- src/logic/playercommand.h	2019-05-07 12:14:02 +0000
+++ src/logic/playercommand.h	2019-05-14 17:38:23 +0000
@@ -24,6 +24,7 @@
 
 #include "economy/flag.h"
 #include "logic/cmd_queue.h"
+#include "logic/map_objects/tribes/constructionsite.h"
 #include "logic/map_objects/tribes/militarysite.h"
 #include "logic/map_objects/tribes/ship.h"
 #include "logic/map_objects/tribes/trainingsite.h"
@@ -823,7 +824,7 @@
 	                  Warehouse& wh,
 	                  bool isworker,
 	                  DescriptionIndex ware,
-	                  Warehouse::StockPolicy policy);
+	                  StockPolicy policy);
 
 	QueueCommandTypes id() const override {
 		return QueueCommandTypes::kSetStockPolicy;
@@ -844,7 +845,7 @@
 	Serial warehouse_;
 	bool isworker_;
 	DescriptionIndex ware_;
-	Warehouse::StockPolicy policy_;
+	StockPolicy policy_;
 };
 
 struct CmdProposeTrade : PlayerCommand {
@@ -868,6 +869,225 @@
 private:
 	Trade trade_;
 };
+
+struct CmdConstructionsiteSoldierCapacity : PlayerCommand {
+	CmdConstructionsiteSoldierCapacity(uint32_t time,
+	                  PlayerNumber p,
+	                  ConstructionSite&,
+	                  uint32_t);
+
+	QueueCommandTypes id() const override {
+		return QueueCommandTypes::kConstructionSiteSoldierCapacity;
+	}
+
+	void execute(Game& game) override;
+
+	// Network (de-)serialization
+	explicit CmdConstructionsiteSoldierCapacity(StreamRead& des);
+	void serialize(StreamWrite& ser) override;
+
+	// Savegame functions
+	CmdConstructionsiteSoldierCapacity();
+	void write(FileWrite&, EditorGameBase&, MapObjectSaver&) override;
+	void read(FileRead&, EditorGameBase&, MapObjectLoader&) override;
+
+private:
+	Serial constructionsite_;
+	uint32_t capacity_;
+};
+
+struct CmdConstructionsitePreferHeroes : PlayerCommand {
+	CmdConstructionsitePreferHeroes(uint32_t time,
+	                  PlayerNumber p,
+	                  ConstructionSite&,
+	                  bool);
+
+	QueueCommandTypes id() const override {
+		return QueueCommandTypes::kConstructionSitePreferHeroes;
+	}
+
+	void execute(Game& game) override;
+
+	// Network (de-)serialization
+	explicit CmdConstructionsitePreferHeroes(StreamRead& des);
+	void serialize(StreamWrite& ser) override;
+
+	// Savegame functions
+	CmdConstructionsitePreferHeroes();
+	void write(FileWrite&, EditorGameBase&, MapObjectSaver&) override;
+	void read(FileRead&, EditorGameBase&, MapObjectLoader&) override;
+
+private:
+	Serial constructionsite_;
+	bool heroes_;
+};
+
+struct CmdConstructionsiteLaunchExpedition : PlayerCommand {
+	CmdConstructionsiteLaunchExpedition(uint32_t time,
+	                  PlayerNumber p,
+	                  ConstructionSite&,
+	                  bool);
+
+	QueueCommandTypes id() const override {
+		return QueueCommandTypes::kConstructionSiteLaunchExpedition;
+	}
+
+	void execute(Game& game) override;
+
+	// Network (de-)serialization
+	explicit CmdConstructionsiteLaunchExpedition(StreamRead& des);
+	void serialize(StreamWrite& ser) override;
+
+	// Savegame functions
+	CmdConstructionsiteLaunchExpedition();
+	void write(FileWrite&, EditorGameBase&, MapObjectSaver&) override;
+	void read(FileRead&, EditorGameBase&, MapObjectLoader&) override;
+
+private:
+	Serial constructionsite_;
+	bool launch_;
+};
+
+struct CmdConstructionsiteStartStop : PlayerCommand {
+	CmdConstructionsiteStartStop(uint32_t time,
+	                  PlayerNumber p,
+	                  ConstructionSite&,
+	                  bool stop);
+
+	QueueCommandTypes id() const override {
+		return QueueCommandTypes::kConstructionSiteStartStop;
+	}
+
+	void execute(Game& game) override;
+
+	// Network (de-)serialization
+	explicit CmdConstructionsiteStartStop(StreamRead& des);
+	void serialize(StreamWrite& ser) override;
+
+	// Savegame functions
+	CmdConstructionsiteStartStop();
+	void write(FileWrite&, EditorGameBase&, MapObjectSaver&) override;
+	void read(FileRead&, EditorGameBase&, MapObjectLoader&) override;
+
+private:
+	Serial constructionsite_;
+	bool stopped_;
+};
+
+struct CmdConstructionsiteStockPolicy : PlayerCommand {
+	CmdConstructionsiteStockPolicy(uint32_t time,
+	                  PlayerNumber p,
+	                  ConstructionSite&,
+	                  WareWorker,
+	                  DescriptionIndex,
+	                  StockPolicy);
+
+	QueueCommandTypes id() const override {
+		return QueueCommandTypes::kConstructionSiteStockPolicy;
+	}
+
+	void execute(Game& game) override;
+
+	// Network (de-)serialization
+	explicit CmdConstructionsiteStockPolicy(StreamRead& des);
+	void serialize(StreamWrite& ser) override;
+
+	// Savegame functions
+	CmdConstructionsiteStockPolicy();
+	void write(FileWrite&, EditorGameBase&, MapObjectSaver&) override;
+	void read(FileRead&, EditorGameBase&, MapObjectLoader&) override;
+
+private:
+	Serial constructionsite_;
+	WareWorker wwtype_;
+	DescriptionIndex index_;
+	StockPolicy policy_;
+};
+
+struct CmdConstructionsiteInputQueuePriority : PlayerCommand {
+	CmdConstructionsiteInputQueuePriority(uint32_t time,
+	                  PlayerNumber p,
+	                  ConstructionSite&,
+	                  WareWorker,
+	                  DescriptionIndex,
+	                  int32_t);
+
+	QueueCommandTypes id() const override {
+		return QueueCommandTypes::kConstructionSiteInputQueuePriority;
+	}
+
+	void execute(Game& game) override;
+
+	// Network (de-)serialization
+	explicit CmdConstructionsiteInputQueuePriority(StreamRead& des);
+	void serialize(StreamWrite& ser) override;
+
+	// Savegame functions
+	CmdConstructionsiteInputQueuePriority();
+	void write(FileWrite&, EditorGameBase&, MapObjectSaver&) override;
+	void read(FileRead&, EditorGameBase&, MapObjectLoader&) override;
+
+private:
+	Serial constructionsite_;
+	WareWorker wwtype_;
+	DescriptionIndex index_;
+	int32_t priority_;
+};
+
+struct CmdConstructionsiteInputQueueMaxFill : PlayerCommand {
+	CmdConstructionsiteInputQueueMaxFill(uint32_t time,
+	                  PlayerNumber p,
+	                  ConstructionSite&,
+	                  WareWorker,
+	                  DescriptionIndex,
+	                  uint32_t);
+
+	QueueCommandTypes id() const override {
+		return QueueCommandTypes::kConstructionSiteInputQueueMaxFill;
+	}
+
+	void execute(Game& game) override;
+
+	// Network (de-)serialization
+	explicit CmdConstructionsiteInputQueueMaxFill(StreamRead& des);
+	void serialize(StreamWrite& ser) override;
+
+	// Savegame functions
+	CmdConstructionsiteInputQueueMaxFill();
+	void write(FileWrite&, EditorGameBase&, MapObjectSaver&) override;
+	void read(FileRead&, EditorGameBase&, MapObjectLoader&) override;
+
+private:
+	Serial constructionsite_;
+	WareWorker wwtype_;
+	DescriptionIndex index_;
+	uint32_t max_fill_;
+};
+
+struct CmdConstructionsiteEnhance : PlayerCommand {
+	CmdConstructionsiteEnhance(uint32_t time,
+	                  PlayerNumber p,
+	                  ConstructionSite&);
+
+	QueueCommandTypes id() const override {
+		return QueueCommandTypes::kConstructionSiteEnhance;
+	}
+
+	void execute(Game& game) override;
+
+	// Network (de-)serialization
+	explicit CmdConstructionsiteEnhance(StreamRead& des);
+	void serialize(StreamWrite& ser) override;
+
+	// Savegame functions
+	CmdConstructionsiteEnhance();
+	void write(FileWrite&, EditorGameBase&, MapObjectSaver&) override;
+	void read(FileRead&, EditorGameBase&, MapObjectLoader&) override;
+
+private:
+	Serial constructionsite_;
+};
+
 }  // namespace Widelands
 
 #endif  // end of include guard: WL_LOGIC_PLAYERCOMMAND_H

=== modified file 'src/logic/queue_cmd_factory.cc'
--- src/logic/queue_cmd_factory.cc	2019-02-23 11:00:49 +0000
+++ src/logic/queue_cmd_factory.cc	2019-05-14 17:38:23 +0000
@@ -80,6 +80,22 @@
 		return *new CmdMilitarySiteSetSoldierPreference();
 	case QueueCommandTypes::kProposeTrade:
 		return *new CmdProposeTrade();
+	case QueueCommandTypes::kConstructionSiteSoldierCapacity:
+		return *new CmdConstructionsiteSoldierCapacity();
+	case QueueCommandTypes::kConstructionSitePreferHeroes:
+		return *new CmdConstructionsitePreferHeroes();
+	case QueueCommandTypes::kConstructionSiteLaunchExpedition:
+		return *new CmdConstructionsiteLaunchExpedition();
+	case QueueCommandTypes::kConstructionSiteStockPolicy:
+		return *new CmdConstructionsiteStockPolicy();
+	case QueueCommandTypes::kConstructionSiteInputQueuePriority:
+		return *new CmdConstructionsiteInputQueuePriority();
+	case QueueCommandTypes::kConstructionSiteInputQueueMaxFill:
+		return *new CmdConstructionsiteInputQueueMaxFill();
+	case QueueCommandTypes::kConstructionSiteEnhance:
+		return *new CmdConstructionsiteEnhance();
+	case QueueCommandTypes::kConstructionSiteStartStop:
+		return *new CmdConstructionsiteStartStop();
 	case QueueCommandTypes::kSinkShip:
 		return *new CmdShipSink();
 	case QueueCommandTypes::kShipCancelExpedition:

=== modified file 'src/logic/queue_cmd_ids.h'
--- src/logic/queue_cmd_ids.h	2019-02-23 11:00:49 +0000
+++ src/logic/queue_cmd_ids.h	2019-05-14 17:38:23 +0000
@@ -73,6 +73,15 @@
 	kMilitarysiteSetSoldierPreference,
 	kProposeTrade,  // 27
 
+	kConstructionSiteStockPolicy,
+	kConstructionSiteSoldierCapacity,
+	kConstructionSitePreferHeroes,
+	kConstructionSiteLaunchExpedition,
+	kConstructionSiteInputQueuePriority,
+	kConstructionSiteInputQueueMaxFill,
+	kConstructionSiteEnhance,
+	kConstructionSiteStartStop, // 35
+
 	kSinkShip = 121,
 	kShipCancelExpedition,
 	kPortStartExpedition,

=== modified file 'src/map_io/map_buildingdata_packet.cc'
--- src/map_io/map_buildingdata_packet.cc	2019-05-12 10:53:08 +0000
+++ src/map_io/map_buildingdata_packet.cc	2019-05-14 17:38:23 +0000
@@ -60,7 +60,7 @@
 
 // Building type package versions
 constexpr uint16_t kCurrentPacketVersionDismantlesite = 1;
-constexpr uint16_t kCurrentPacketVersionConstructionsite = 3;
+constexpr uint16_t kCurrentPacketVersionConstructionsite = 4;
 constexpr uint16_t kCurrentPacketPFBuilding = 1;
 // Responsible for warehouses and expedition bootstraps
 constexpr uint16_t kCurrentPacketVersionWarehouse = 7;
@@ -269,7 +269,7 @@
                                                   MapObjectLoader& mol) {
 	try {
 		uint16_t const packet_version = fr.unsigned_16();
-		if (packet_version >= kCurrentPacketVersionConstructionsite) {
+		if (packet_version >= 3 && packet_version <= kCurrentPacketVersionConstructionsite) {
 			read_partially_finished_building(constructionsite, fr, game, mol);
 
 			for (ConstructionSite::Wares::iterator wares_iter = constructionsite.wares_.begin();
@@ -279,6 +279,18 @@
 			}
 
 			constructionsite.fetchfromflag_ = fr.signed_32();
+
+			if (packet_version >= 4) {
+				const uint32_t intermediates = fr.unsigned_32();
+				for (uint32_t i = 0; i < intermediates; ++i) {
+					constructionsite.info_.intermediates.push_back(game.tribes().get_building_descr(
+							game.tribes().building_index(fr.c_string())));
+				}
+				constructionsite.settings_.reset(BuildingSettings::load(game,
+						constructionsite.owner().tribe(), fr));
+			} else {
+				constructionsite.settings_.reset(nullptr);
+			}
 		} else {
 			throw UnhandledVersionError("MapBuildingdataPacket - Constructionsite", packet_version,
 			                            kCurrentPacketVersionConstructionsite);
@@ -321,7 +333,7 @@
 			while (fr.unsigned_8()) {
 				const DescriptionIndex& id = tribe.ware_index(fr.c_string());
 				Quantity amount = fr.unsigned_32();
-				Warehouse::StockPolicy policy = static_cast<Warehouse::StockPolicy>(fr.unsigned_8());
+				StockPolicy policy = static_cast<StockPolicy>(fr.unsigned_8());
 
 				if (game.tribes().ware_exists(id)) {
 					warehouse.insert_wares(id, amount);
@@ -331,7 +343,7 @@
 			while (fr.unsigned_8()) {
 				const DescriptionIndex& id = tribe.worker_index(fr.c_string());
 				uint32_t amount = fr.unsigned_32();
-				Warehouse::StockPolicy policy = static_cast<Warehouse::StockPolicy>(fr.unsigned_8());
+				StockPolicy policy = static_cast<StockPolicy>(fr.unsigned_8());
 
 				if (game.tribes().worker_exists(id)) {
 					warehouse.insert_workers(id, amount);
@@ -961,6 +973,14 @@
 	write_partially_finished_building(constructionsite, fw, game, mos);
 
 	fw.signed_32(constructionsite.fetchfromflag_);
+
+	fw.unsigned_32(constructionsite.info_.intermediates.size());
+	for (const BuildingDescr* d : constructionsite.info_.intermediates) {
+		fw.c_string(d->name().c_str());
+	}
+
+	assert(constructionsite.settings_);
+	constructionsite.settings_->save(game, fw);
 }
 
 void MapBuildingdataPacket::write_dismantlesite(const DismantleSite& dms,

=== modified file 'src/notifications/note_ids.h'
--- src/notifications/note_ids.h	2019-02-23 11:00:49 +0000
+++ src/notifications/note_ids.h	2019-05-14 17:38:23 +0000
@@ -29,6 +29,7 @@
 	ChatMessage,
 	LogMessage,
 	Immovable,
+	ConstructionsiteEnhanced,
 	FieldPossession,
 	FieldTerrainChanged,
 	ProductionSiteOutOfResources,

=== modified file 'src/scripting/lua_map.cc'
--- src/scripting/lua_map.cc	2019-05-04 10:47:44 +0000
+++ src/scripting/lua_map.cc	2019-05-14 17:38:23 +0000
@@ -4871,39 +4871,39 @@
 }
 
 // Transforms the given warehouse policy to a string which is used by the lua code
-inline void wh_policy_to_string(lua_State* L, Warehouse::StockPolicy p) {
+inline void wh_policy_to_string(lua_State* L, StockPolicy p) {
 	switch (p) {
-	case Warehouse::StockPolicy::kNormal:
+	case StockPolicy::kNormal:
 		lua_pushstring(L, "normal");
 		break;
-	case Warehouse::StockPolicy::kPrefer:
+	case StockPolicy::kPrefer:
 		lua_pushstring(L, "prefer");
 		break;
-	case Warehouse::StockPolicy::kDontStock:
+	case StockPolicy::kDontStock:
 		lua_pushstring(L, "dontstock");
 		break;
-	case Warehouse::StockPolicy::kRemove:
+	case StockPolicy::kRemove:
 		lua_pushstring(L, "remove");
 		break;
 	}
 }
 // Transforms the given string from the lua code to a warehouse policy
-inline Warehouse::StockPolicy string_to_wh_policy(lua_State* L, uint32_t index) {
+inline StockPolicy string_to_wh_policy(lua_State* L, uint32_t index) {
 	std::string str = luaL_checkstring(L, index);
 	if (str == "normal")
-		return Warehouse::StockPolicy::kNormal;
+		return StockPolicy::kNormal;
 	else if (str == "prefer")
-		return Warehouse::StockPolicy::kPrefer;
+		return StockPolicy::kPrefer;
 	else if (str == "dontstock")
-		return Warehouse::StockPolicy::kDontStock;
+		return StockPolicy::kDontStock;
 	else if (str == "remove")
-		return Warehouse::StockPolicy::kRemove;
+		return StockPolicy::kRemove;
 	else
 		report_error(L, "<%s> is no valid warehouse policy!", str.c_str());
 }
 
 inline bool
-do_set_ware_policy(Warehouse* wh, const DescriptionIndex idx, const Warehouse::StockPolicy p) {
+do_set_ware_policy(Warehouse* wh, const DescriptionIndex idx, const StockPolicy p) {
 	wh->set_ware_policy(idx, p);
 	return true;
 }
@@ -4913,7 +4913,7 @@
  * If the no ware with the given name exists for the tribe of the warehouse, return false.
  */
 inline bool
-do_set_ware_policy(Warehouse* wh, const std::string& name, const Warehouse::StockPolicy p) {
+do_set_ware_policy(Warehouse* wh, const std::string& name, const StockPolicy p) {
 	const TribeDescr& tribe = wh->owner().tribe();
 	DescriptionIndex idx = tribe.ware_index(name);
 	if (!tribe.has_ware(idx)) {
@@ -4923,7 +4923,7 @@
 }
 
 inline bool
-do_set_worker_policy(Warehouse* wh, const DescriptionIndex idx, const Warehouse::StockPolicy p) {
+do_set_worker_policy(Warehouse* wh, const DescriptionIndex idx, const StockPolicy p) {
 	const TribeDescr& tribe = wh->owner().tribe();
 	// If the worker does not cost anything, ignore it
 	// Otherwise, an unlimited stream of carriers might leave the warehouse
@@ -4942,7 +4942,7 @@
  * If no worker with the given name exists for the tribe of the warehouse, return false.
  */
 inline bool
-do_set_worker_policy(Warehouse* wh, const std::string& name, const Warehouse::StockPolicy p) {
+do_set_worker_policy(Warehouse* wh, const std::string& name, const StockPolicy p) {
 	const TribeDescr& tribe = wh->owner().tribe();
 	DescriptionIndex idx = tribe.worker_index(name);
 	if (!tribe.has_worker(idx)) {
@@ -4972,7 +4972,7 @@
 		report_error(L, "Wrong number of arguments to set_warehouse_policies!");
 
 	Warehouse* wh = get(L, get_egbase(L));
-	Warehouse::StockPolicy p = string_to_wh_policy(L, -1);
+	StockPolicy p = string_to_wh_policy(L, -1);
 	lua_pop(L, 1);
 	const TribeDescr& tribe = wh->owner().tribe();
 

=== modified file 'src/wui/actionconfirm.cc'
--- src/wui/actionconfirm.cc	2019-02-23 11:00:49 +0000
+++ src/wui/actionconfirm.cc	2019-05-14 17:38:23 +0000
@@ -92,7 +92,8 @@
 struct EnhanceConfirm : public ActionConfirm {
 	EnhanceConfirm(InteractivePlayer& parent,
 	               Widelands::Building& building,
-	               const Widelands::DescriptionIndex& id);
+	               const Widelands::DescriptionIndex& id,
+                   bool still_under_construction);
 
 	void think() override;
 	void ok() override;
@@ -100,6 +101,7 @@
 private:
 	// Do not make this a reference - it is a stack variable in the caller
 	const Widelands::DescriptionIndex id_;
+	bool still_under_construction_;
 };
 
 /**
@@ -259,7 +261,8 @@
 */
 EnhanceConfirm::EnhanceConfirm(InteractivePlayer& parent,
                                Widelands::Building& building,
-                               const Widelands::DescriptionIndex& id)
+                               const Widelands::DescriptionIndex& id,
+                          bool still_under_construction)
    : ActionConfirm(
         parent,
         _("Enhance building?"),
@@ -270,7 +273,8 @@
               .str() :
            _("Do you really want to enhance this building?"),
         building),
-     id_(id) {
+     id_(id),
+     still_under_construction_(still_under_construction) {
 	// Nothing special to do
 }
 
@@ -284,7 +288,7 @@
 	upcast(Widelands::Building, building, object_.get(egbase));
 
 	if (!building || !iaplayer().can_act(building->owner().player_number()) ||
-	    !(building->get_playercaps() & Widelands::Building::PCap_Enhancable))
+	    !(still_under_construction_ || (building->get_playercaps() & Widelands::Building::PCap_Enhancable)))
 		die();
 }
 
@@ -293,11 +297,18 @@
  */
 void EnhanceConfirm::ok() {
 	Widelands::Game& game = iaplayer().game();
-	upcast(Widelands::Building, building, object_.get(game));
 
-	if (building && iaplayer().can_act(building->owner().player_number()) &&
-	    (building->get_playercaps() & Widelands::Building::PCap_Enhancable)) {
-		game.send_player_enhance_building(*building, id_);
+	if (still_under_construction_) {
+		upcast(Widelands::ConstructionSite, cs, object_.get(game));
+		if (cs && iaplayer().can_act(cs->owner().player_number())) {
+			game.send_player_constructionsite_enhance(*cs);
+		}
+	} else {
+		upcast(Widelands::Building, building, object_.get(game));
+		if (building && iaplayer().can_act(building->owner().player_number()) &&
+			(building->get_playercaps() & Widelands::Building::PCap_Enhancable)) {
+			game.send_player_enhance_building(*building, id_);
+		}
 	}
 
 	die();
@@ -419,8 +430,9 @@
  */
 void show_enhance_confirm(InteractivePlayer& player,
                           Widelands::Building& building,
-                          const Widelands::DescriptionIndex& id) {
-	new EnhanceConfirm(player, building, id);
+                          const Widelands::DescriptionIndex& id,
+                          bool constructionsite) {
+	new EnhanceConfirm(player, building, id, constructionsite);
 }
 
 /**

=== modified file 'src/wui/actionconfirm.h'
--- src/wui/actionconfirm.h	2019-02-23 11:00:49 +0000
+++ src/wui/actionconfirm.h	2019-05-14 17:38:23 +0000
@@ -39,7 +39,8 @@
 
 void show_enhance_confirm(InteractivePlayer& player,
                           Widelands::Building& building,
-                          const Widelands::DescriptionIndex& id);
+                          const Widelands::DescriptionIndex& id,
+                          bool still_under_construction = false);
 
 // Ship confirm windows
 void show_ship_sink_confirm(InteractivePlayer& player, Widelands::Ship& ship);

=== modified file 'src/wui/constructionsitewindow.cc'
--- src/wui/constructionsitewindow.cc	2019-02-23 11:00:49 +0000
+++ src/wui/constructionsitewindow.cc	2019-05-14 17:38:23 +0000
@@ -22,9 +22,256 @@
 #include <boost/format.hpp>
 
 #include "graphic/graphic.h"
+#include "wui/actionconfirm.h"
 #include "wui/inputqueuedisplay.h"
+#include "wui/interactive_player.h"
 
 static const char pic_tab_wares[] = "images/wui/buildings/menu_tab_wares.png";
+static const char pic_tab_workers[] = "images/wui/buildings/menu_tab_workers.png";
+static const char pic_tab_settings[] = "images/wui/menus/menu_stock.png";
+static const char pic_max_fill_indicator[] = "images/wui/buildings/max_fill_indicator.png";
+static const char pic_priority_low[] = "images/wui/buildings/low_priority_button.png";
+static const char pic_priority_normal[] = "images/wui/buildings/normal_priority_button.png";
+static const char pic_priority_high[] = "images/wui/buildings/high_priority_button.png";
+static const char pic_stock_policy_prefer[] = "images/wui/buildings/stock_policy_prefer.png";
+static const char pic_stock_policy_dontstock[] = "images/wui/buildings/stock_policy_dontstock.png";
+static const char pic_stock_policy_remove[] = "images/wui/buildings/stock_policy_remove.png";
+static const char pic_stock_policy_button_normal[] = "images/wui/buildings/stock_policy_button_normal.png";
+static const char pic_stock_policy_button_prefer[] = "images/wui/buildings/stock_policy_button_prefer.png";
+static const char pic_stock_policy_button_dontstock[] = "images/wui/buildings/stock_policy_button_dontstock.png";
+static const char pic_stock_policy_button_remove[] = "images/wui/buildings/stock_policy_button_remove.png";
+
+constexpr uint32_t kFakeInputQueueWareWidth = WARE_MENU_PIC_WIDTH + 2;
+constexpr uint32_t kFakeInputQueueWareHeight = WARE_MENU_PIC_HEIGHT + 4;
+
+ConstructionSiteWindow::FakeInputQueue::FakeInputQueue(Panel* parent,
+               int32_t x,
+               int32_t y,
+               bool can_act,
+               Widelands::ConstructionSite& cs,
+               Widelands::WareWorker ww,
+               Widelands::DescriptionIndex di)
+		: UI::Box(parent, x, y, UI::Box::Horizontal),
+		constructionsite_(cs),
+		settings_(*dynamic_cast<Widelands::ProductionsiteSettings*>(cs.get_settings())),
+		type_(ww),
+		index_(di),
+		max_fill_indicator_(g_gr->images().get(pic_max_fill_indicator)) {
+	max_fill_ = get_settings().max_fill;
+
+	const Widelands::Tribes& tribes = cs.owner().egbase().tribes();
+	const Widelands::MapObjectDescr* w_descr = nullptr;
+	if (type_ == Widelands::wwWARE) {
+		w_descr = tribes.get_ware_descr(index_);
+	} else {
+		w_descr = tribes.get_worker_descr(index_);
+	}
+	assert(w_descr);
+	set_tooltip(w_descr->descname());
+	icon_ = w_descr->icon();
+
+	UI::Button& decrease = *new UI::Button(
+			this, "decrease_max_fill", 0, 0, kFakeInputQueueWareWidth, kFakeInputQueueWareHeight,
+			UI::ButtonStyle::kWuiMenu, g_gr->images().get("images/ui_basic/scrollbar_left.png"),
+			_("Decrease the number of items to initially store here"));
+	UI::Button& increase = *new UI::Button(
+			this, "increase_max_fill", 0, 0, kFakeInputQueueWareWidth, kFakeInputQueueWareHeight,
+			UI::ButtonStyle::kWuiMenu, g_gr->images().get("images/ui_basic/scrollbar_right.png"),
+			_("Increase the number of items to initially store here"));
+	decrease.sigclicked.connect(boost::bind(&ConstructionSiteWindow::FakeInputQueue::change_fill, this, true));
+	increase.sigclicked.connect(boost::bind(&ConstructionSiteWindow::FakeInputQueue::change_fill, this, false));
+	decrease.set_repeating(true);
+	increase.set_repeating(true);
+	add(&decrease);
+	add_space(kFakeInputQueueWareWidth * max_fill_ + 8);
+	add(&increase);
+	if (type_ == Widelands::wwWARE) {
+		priority_low_ = new UI::Button(this, "priority_low", 0, 0,
+				kFakeInputQueueWareWidth, kFakeInputQueueWareHeight,
+				UI::ButtonStyle::kWuiMenu, g_gr->images().get(pic_priority_low),
+				_("Lowest priority"));
+		priority_normal_ = new UI::Button(this, "priority_normal", 0, 0,
+				kFakeInputQueueWareWidth, kFakeInputQueueWareHeight,
+				UI::ButtonStyle::kWuiMenu, g_gr->images().get(pic_priority_normal),
+				_("Normal priority"));
+		priority_high_ = new UI::Button(this, "priority_high", 0, 0,
+				kFakeInputQueueWareWidth, kFakeInputQueueWareHeight,
+				UI::ButtonStyle::kWuiMenu, g_gr->images().get(pic_priority_high),
+				_("Highest priority"));
+		add_space(8);
+		add(priority_low_, UI::Box::Resizing::kAlign, UI::Align::kCenter);
+		add(priority_normal_, UI::Box::Resizing::kAlign, UI::Align::kCenter);
+		add(priority_high_, UI::Box::Resizing::kAlign, UI::Align::kCenter);
+		if (can_act) {
+			priority_low_->sigclicked.connect(boost::bind(&ConstructionSiteWindow::FakeInputQueue::change_priority,
+					this, Widelands::kPriorityLow));
+			priority_normal_->sigclicked.connect(boost::bind(&ConstructionSiteWindow::FakeInputQueue::change_priority,
+					this, Widelands::kPriorityNormal));
+			priority_high_->sigclicked.connect(boost::bind(&ConstructionSiteWindow::FakeInputQueue::change_priority,
+					this, Widelands::kPriorityHigh));
+		}
+	}
+	decrease.set_enabled(can_act);
+	increase.set_enabled(can_act);
+}
+
+void ConstructionSiteWindow::FakeInputQueue::change_priority(int32_t p) {
+	Widelands::Game& game = dynamic_cast<Widelands::Game&>(constructionsite_.get_owner()->egbase());
+	if (SDL_GetModState() & KMOD_CTRL) {
+		for (const auto& pair : settings_.ware_queues) {
+			game.send_player_constructionsite_input_queue_priority(constructionsite_,
+					Widelands::wwWARE, pair.first, p);
+		}
+		for (const auto& pair : settings_.worker_queues) {
+			game.send_player_constructionsite_input_queue_priority(constructionsite_,
+					Widelands::wwWORKER, pair.first, p);
+		}
+	} else {
+		game.send_player_constructionsite_input_queue_priority(constructionsite_, type_, index_, p);
+	}
+}
+
+void ConstructionSiteWindow::FakeInputQueue::change_fill(bool lower) {
+	Widelands::Game& game = dynamic_cast<Widelands::Game&>(constructionsite_.get_owner()->egbase());
+	if (SDL_GetModState() & KMOD_SHIFT) {
+		for (const auto& pair : settings_.ware_queues) {
+			if (SDL_GetModState() & KMOD_CTRL) {
+				game.send_player_constructionsite_input_queue_max_fill(constructionsite_,
+						Widelands::wwWARE, pair.first, lower ? 0 : pair.second.max_fill);
+			} else if (lower && pair.second.desired_fill > 0) {
+				game.send_player_constructionsite_input_queue_max_fill(constructionsite_,
+						Widelands::wwWARE, pair.first, pair.second.desired_fill - 1);
+			} else if (!lower && pair.second.desired_fill < pair.second.max_fill) {
+				game.send_player_constructionsite_input_queue_max_fill(constructionsite_,
+						Widelands::wwWARE, pair.first, pair.second.desired_fill + 1);
+			}
+		}
+		for (const auto& pair : settings_.worker_queues) {
+			if (SDL_GetModState() & KMOD_CTRL) {
+				game.send_player_constructionsite_input_queue_max_fill(constructionsite_,
+						Widelands::wwWORKER, pair.first, lower ? 0 : pair.second.max_fill);
+			} else if (lower && pair.second.desired_fill > 0) {
+				game.send_player_constructionsite_input_queue_max_fill(constructionsite_,
+						Widelands::wwWORKER, pair.first, pair.second.desired_fill - 1);
+			} else if (!lower && pair.second.desired_fill < pair.second.max_fill) {
+				game.send_player_constructionsite_input_queue_max_fill(constructionsite_,
+						Widelands::wwWORKER, pair.first, pair.second.desired_fill + 1);
+			}
+		}
+	} else {
+		const uint32_t fill = get_settings().desired_fill;
+		if (SDL_GetModState() & KMOD_CTRL) {
+			game.send_player_constructionsite_input_queue_max_fill(constructionsite_, type_, index_,
+					lower ? 0 : max_fill_);
+		} else if (lower && fill > 0) {
+			game.send_player_constructionsite_input_queue_max_fill(constructionsite_, type_, index_, fill - 1);
+		} else if (!lower && fill < max_fill_) {
+			game.send_player_constructionsite_input_queue_max_fill(constructionsite_, type_, index_, fill + 1);
+		}
+	}
+}
+
+const Widelands::ProductionsiteSettings::InputQueueSetting& ConstructionSiteWindow::FakeInputQueue::get_settings() const {
+	switch (type_) {
+		case Widelands::wwWARE:
+			for (const auto& pair : settings_.ware_queues) {
+				if (pair.first == index_) {
+					return pair.second;
+				}
+			}
+			NEVER_HERE();
+		case Widelands::wwWORKER:
+			for (const auto& pair : settings_.worker_queues) {
+				if (pair.first == index_) {
+					return pair.second;
+				}
+			}
+			NEVER_HERE();
+		default:
+			NEVER_HERE();
+	}
+}
+
+void ConstructionSiteWindow::FakeInputQueue::think() {
+	UI::Box::think();
+	if (priority_normal_) {
+		assert(priority_high_);
+		assert(priority_low_);
+		const int32_t priority = get_settings().priority;
+		priority_high_->set_enabled(priority != Widelands::kPriorityHigh);
+		priority_normal_->set_enabled(priority != Widelands::kPriorityNormal);
+		priority_low_->set_enabled(priority != Widelands::kPriorityLow);
+	}
+#ifndef NDEBUG
+	else {
+		assert(!priority_high_);
+		assert(!priority_low_);
+	}
+#endif
+}
+
+void ConstructionSiteWindow::FakeInputQueue::draw(RenderTarget& dst) {
+	UI::Box::draw(dst);
+
+	Vector2i point = Vector2i::zero();
+	point.x = kFakeInputQueueWareWidth + 4;
+	point.y = (kFakeInputQueueWareHeight - icon_->height()) / 2;
+
+	const uint32_t fill = get_settings().desired_fill;
+	uint32_t draw_yes = fill;
+	uint32_t draw_no = max_fill_ - draw_yes;
+	for (; draw_yes; --draw_yes, point.x += kFakeInputQueueWareWidth) {
+		dst.blitrect(Vector2i(point.x, point.y), icon_, Recti(0, 0, icon_->width(), icon_->height()),
+			         BlendMode::UseAlpha);
+	}
+	for (; draw_no; --draw_no, point.x += kFakeInputQueueWareWidth) {
+		dst.blitrect_scale_monochrome(Rectf(point.x, point.y, icon_->width(), icon_->height()), icon_,
+			                          Recti(0, 0, icon_->width(), icon_->height()),
+			                          RGBAColor(191, 191, 191, 127));
+	}
+
+	point.x = 4 + kFakeInputQueueWareWidth + (fill * kFakeInputQueueWareWidth) - max_fill_indicator_->width() / 2;
+	// Unsigned arithmetic...
+	point.y = kFakeInputQueueWareHeight;
+	point.y -= max_fill_indicator_->height();
+	point.y /= 2;
+	dst.blit(point, max_fill_indicator_);
+}
+
+ConstructionSiteWindow::FakeWaresDisplay::FakeWaresDisplay(UI::Panel* parent,
+														  bool can_act,
+														  Widelands::ConstructionSite& cs,
+														  Widelands::WareWorker type)
+	: WaresDisplay(parent, 0, 0, cs.owner().tribe(), type, can_act),
+	  settings_(*dynamic_cast<Widelands::WarehouseSettings*>(cs.get_settings())) {
+}
+
+void ConstructionSiteWindow::FakeWaresDisplay::draw_ware(RenderTarget& dst, Widelands::DescriptionIndex ware) {
+	WaresDisplay::draw_ware(dst, ware);
+
+	const auto& map = get_type() == Widelands::wwWARE ? settings_.ware_preferences : settings_.worker_preferences;
+	const auto it = map.find(ware);
+	if (it == map.end()) {
+		return;
+	}
+	const Image* pic = nullptr;
+	switch (it->second) {
+	case Widelands::StockPolicy::kPrefer:
+		pic = g_gr->images().get(pic_stock_policy_prefer);
+		break;
+	case Widelands::StockPolicy::kDontStock:
+		pic = g_gr->images().get(pic_stock_policy_dontstock);
+		break;
+	case Widelands::StockPolicy::kRemove:
+		pic = g_gr->images().get(pic_stock_policy_remove);
+		break;
+	case Widelands::StockPolicy::kNormal:
+		// No icon for the normal policy
+		return;
+	}
+	assert(pic);
+	dst.blit(ware_position(ware), pic);
+}
 
 ConstructionSiteWindow::ConstructionSiteWindow(InteractiveGameBase& parent,
                                                UI::UniqueWindow::Registry& reg,
@@ -33,7 +280,19 @@
                                                bool workarea_preview_wanted)
    : BuildingWindow(parent, reg, cs, cs.building(), avoid_fastclick),
      construction_site_(&cs),
-     progress_(nullptr) {
+     progress_(nullptr),
+		cs_enhance_(nullptr),
+		cs_launch_expedition_(nullptr),
+		cs_prefer_heroes_(nullptr),
+		cs_soldier_capacity_(nullptr),
+		cs_stopped_(nullptr),
+		cs_warehouse_wares_(nullptr),
+		cs_warehouse_workers_(nullptr),
+		cs_warehouse_stock_policy_normal_(nullptr),
+		cs_warehouse_stock_policy_prefer_(nullptr),
+		cs_warehouse_stock_policy_dontstock_(nullptr),
+		cs_warehouse_stock_policy_remove_(nullptr),
+		cs_warehouse_tabs_(nullptr) {
 	init(avoid_fastclick, workarea_preview_wanted);
 }
 
@@ -59,10 +318,182 @@
 
 	get_tabs()->add("wares", g_gr->images().get(pic_tab_wares), &box, _("Building materials"));
 
+	if (construction_site->get_settings()) {
+		const bool can_act = igbase()->can_act(construction_site->owner().player_number());
+		// Create the settings. Since we don't access an actual building, we create
+		// a simplified faksimile of the later building window that contains only
+		// the relevant options.
+		UI::Box& settings_box = *new UI::Box(get_tabs(), 0, 0, UI::Box::Vertical);
+		if (upcast(Widelands::ProductionsiteSettings, ps, construction_site->get_settings())) {
+			for (const auto& pair : ps->ware_queues) {
+				FakeInputQueue* queue = new FakeInputQueue(&settings_box, 0, 0, can_act,
+						*construction_site, Widelands::wwWARE, pair.first);
+				settings_box.add(queue);
+				settings_box.add_space(8);
+				cs_ware_queues_.push_back(queue);
+			}
+			for (const auto& pair : ps->worker_queues) {
+				FakeInputQueue* queue = new FakeInputQueue(&settings_box, 0, 0, can_act,
+						*construction_site, Widelands::wwWORKER, pair.first);
+				settings_box.add(queue);
+				settings_box.add_space(8);
+				cs_ware_queues_.push_back(queue);
+			}
+			if (upcast(Widelands::TrainingsiteSettings, ts, ps)) {
+				cs_soldier_capacity_ = new UI::SpinBox(&settings_box, 0, 0, 300, 100,
+						ts->desired_capacity, 0, ts->max_capacity, UI::PanelStyle::kWui, _("Soldier capacity:"));
+				settings_box.add(cs_soldier_capacity_);
+				settings_box.add_space(8);
+				cs_soldier_capacity_->changed.connect([this]() {
+					igbase()->game().send_player_constructionsite_soldier_capacity(
+							*construction_site_.get(igbase()->egbase()), cs_soldier_capacity_->get_value());
+				});
+				for (UI::Button* b : cs_soldier_capacity_->get_buttons()) {
+					b->set_enabled(can_act);
+				}
+			}
+			cs_stopped_ = new UI::Checkbox(&settings_box, Vector2i::zero(),
+				 _("Stopped"),
+				 _("Stop this building’s work after completion"));
+			cs_stopped_->changed.connect([this]() {
+				igbase()->game().send_player_constructionsite_startstop(
+						*construction_site_.get(igbase()->egbase()), cs_stopped_->get_state());
+			});
+			settings_box.add(cs_stopped_, UI::Box::Resizing::kFullSize);
+			settings_box.add_space(8);
+			cs_stopped_->set_enabled(can_act);
+		} else if (upcast(Widelands::MilitarysiteSettings, ms, construction_site->get_settings())) {
+			cs_soldier_capacity_ = new UI::SpinBox(&settings_box, 0, 0, 300, 100,
+					ms->desired_capacity, 1, ms->max_capacity, UI::PanelStyle::kWui, _("Soldier capacity:"));
+			settings_box.add(cs_soldier_capacity_);
+			settings_box.add_space(8);
+			cs_soldier_capacity_->changed.connect([this]() {
+				igbase()->game().send_player_constructionsite_soldier_capacity(*construction_site_.get(igbase()->egbase()),
+						cs_soldier_capacity_->get_value());
+			});
+			for (UI::Button* b : cs_soldier_capacity_->get_buttons()) {
+				b->set_enabled(can_act);
+			}
+			cs_prefer_heroes_ = new UI::Checkbox(&settings_box, Vector2i::zero(),
+				 _("Prefer heroes"),
+				 _("Prefer heroes over rookies"));
+			cs_prefer_heroes_->changed.connect([this]() {
+				igbase()->game().send_player_constructionsite_prefer_heroes(*construction_site_.get(igbase()->egbase()),
+						cs_prefer_heroes_->get_state());
+			});
+			cs_prefer_heroes_->set_enabled(can_act);
+			settings_box.add(cs_prefer_heroes_, UI::Box::Resizing::kFullSize);
+			settings_box.add_space(8);
+		} else if (upcast(Widelands::WarehouseSettings, ws, construction_site->get_settings())) {
+			cs_warehouse_tabs_ = new UI::TabPanel(&settings_box, UI::TabPanelStyle::kWuiLight);
+			cs_warehouse_wares_ = new FakeWaresDisplay(
+					cs_warehouse_tabs_, can_act, *construction_site, Widelands::wwWARE);
+			cs_warehouse_workers_ = new FakeWaresDisplay(
+					cs_warehouse_tabs_, can_act, *construction_site, Widelands::wwWORKER);
+			cs_warehouse_tabs_->add("tab_wares", g_gr->images().get(pic_tab_wares),
+					cs_warehouse_wares_, _("Wares"));
+			cs_warehouse_tabs_->add("tab_workers", g_gr->images().get(pic_tab_workers),
+					cs_warehouse_workers_, _("Workers"));
+			settings_box.add(cs_warehouse_tabs_, UI::Box::Resizing::kFullSize);
+			settings_box.add_space(8);
+			UI::Box& buttonsbox = *new UI::Box(&settings_box, 0, 0, UI::Box::Horizontal);
+			settings_box.add(&buttonsbox, UI::Box::Resizing::kAlign, UI::Align::kCenter);
+			settings_box.add_space(8);
+			cs_warehouse_stock_policy_normal_ = new UI::Button(&buttonsbox, "stock_policy_normal", 0, 0, 34, 34,
+					UI::ButtonStyle::kWuiMenu, g_gr->images().get(pic_stock_policy_button_normal),
+					_("Normal policy"));
+			cs_warehouse_stock_policy_prefer_ = new UI::Button(&buttonsbox, "stock_policy_prefer", 0, 0, 34, 34,
+					UI::ButtonStyle::kWuiMenu, g_gr->images().get(pic_stock_policy_button_prefer),
+					_("Preferably store selected wares here"));
+			cs_warehouse_stock_policy_dontstock_ = new UI::Button(&buttonsbox, "stock_policy_dontstock", 0, 0, 34, 34,
+					UI::ButtonStyle::kWuiMenu, g_gr->images().get(pic_stock_policy_button_dontstock),
+					_("Do not store selected wares here"));
+			cs_warehouse_stock_policy_remove_ = new UI::Button(&buttonsbox, "stock_policy_remove", 0, 0, 34, 34,
+					UI::ButtonStyle::kWuiMenu, g_gr->images().get(pic_stock_policy_button_remove),
+					_("Remove selected wares from here"));
+			cs_warehouse_stock_policy_remove_->sigclicked.connect(
+					boost::bind(&ConstructionSiteWindow::change_policy, this, Widelands::StockPolicy::kRemove));
+			cs_warehouse_stock_policy_dontstock_->sigclicked.connect(
+					boost::bind(&ConstructionSiteWindow::change_policy, this, Widelands::StockPolicy::kDontStock));
+			cs_warehouse_stock_policy_prefer_->sigclicked.connect(
+					boost::bind(&ConstructionSiteWindow::change_policy, this, Widelands::StockPolicy::kPrefer));
+			cs_warehouse_stock_policy_normal_->sigclicked.connect(
+					boost::bind(&ConstructionSiteWindow::change_policy, this, Widelands::StockPolicy::kNormal));
+			buttonsbox.add(cs_warehouse_stock_policy_normal_);
+			buttonsbox.add_space(8);
+			buttonsbox.add(cs_warehouse_stock_policy_prefer_);
+			buttonsbox.add_space(8);
+			buttonsbox.add(cs_warehouse_stock_policy_dontstock_);
+			buttonsbox.add_space(8);
+			buttonsbox.add(cs_warehouse_stock_policy_remove_);
+			if (construction_site->get_info().becomes->get_isport()) {
+				cs_launch_expedition_ = new UI::Checkbox(&settings_box, Vector2i::zero(),
+					 _("Launch expedition"),
+					 _("Start an expedition from this port after completion"));
+				cs_launch_expedition_->changed.connect([this]() {
+					igbase()->game().send_player_constructionsite_launch_expedition(
+							*construction_site_.get(igbase()->egbase()), cs_launch_expedition_->get_state());
+				});
+				settings_box.add(cs_launch_expedition_, UI::Box::Resizing::kFullSize);
+				settings_box.add_space(8);
+				cs_launch_expedition_->set_enabled(can_act);
+			}
+		} else {
+			NEVER_HERE();
+		}
+
+		if (can_act && construction_site->get_info().becomes->enhancement() != Widelands::INVALID_INDEX) {
+			const Widelands::BuildingDescr& building_descr = *igbase()->egbase().tribes().get_building_descr(
+				construction_site->get_info().becomes->enhancement());
+			std::string enhance_tooltip =
+			   (boost::format(_("Enhance to %s")) % building_descr.descname().c_str()).str() +
+			   "<br><font size=11>" + _("Construction costs:") + "</font><br>" +
+			   waremap_to_richtext(construction_site->owner().tribe(), building_descr.enhancement_cost());
+			cs_enhance_ = new UI::Button(&settings_box, "enhance", 0, 0, 34, 34, UI::ButtonStyle::kWuiMenu,
+			                  building_descr.icon(), enhance_tooltip);
+			cs_enhance_->sigclicked.connect([this, construction_site] {
+				if (SDL_GetModState() & KMOD_CTRL) {
+					igbase()->game().send_player_constructionsite_enhance(*construction_site);
+				} else {
+				show_enhance_confirm(dynamic_cast<InteractivePlayer&>(*igbase()),
+						*construction_site, construction_site->get_info().becomes->enhancement(), true);
+				}
+			});
+			settings_box.add(cs_enhance_, UI::Box::Resizing::kAlign, UI::Align::kCenter);
+			settings_box.add_space(8);
+		}
+		if (settings_box.get_nritems()) {
+			get_tabs()->add("settings", g_gr->images().get(pic_tab_settings),
+					&settings_box, _("Settings to apply after construction"));
+		}
+	}
+
 	set_title((boost::format("(%s)") % construction_site->building().descname()).str());
 	think();
 }
 
+void ConstructionSiteWindow::change_policy(Widelands::StockPolicy p) {
+	Widelands::ConstructionSite* construction_site = construction_site_.get(igbase()->egbase());
+	assert(construction_site);
+	upcast(Widelands::WarehouseSettings, ws, construction_site->get_settings());
+	assert(ws);
+	if (cs_warehouse_tabs_->active() == 0) {
+		for (const auto& pair : ws->ware_preferences) {
+			if (cs_warehouse_wares_->ware_selected(pair.first)) {
+				igbase()->game().send_player_constructionsite_stock_policy(*construction_site,
+						Widelands::wwWARE, pair.first, p);
+			}
+		}
+	} else {
+		for (const auto& pair : ws->worker_preferences) {
+			if (cs_warehouse_workers_->ware_selected(pair.first)) {
+				igbase()->game().send_player_constructionsite_stock_policy(*construction_site,
+						Widelands::wwWORKER, pair.first, p);
+			}
+		}
+	}
+}
+
 /*
 ===============
 Make sure the window is redrawn when necessary.
@@ -77,5 +508,30 @@
 	if (construction_site == nullptr) {
 		return;
 	}
+
 	progress_->set_state(construction_site->get_built_per64k());
+
+	// FakeInputQueue and FakeWaresDisplay update themselves – we need to refresh the other settings
+	if (upcast(Widelands::ProductionsiteSettings, ps, construction_site->get_settings())) {
+		assert(cs_stopped_);
+		cs_stopped_->set_state(ps->stopped);
+	}
+	if (upcast(Widelands::TrainingsiteSettings, ts, construction_site->get_settings())) {
+		assert(cs_soldier_capacity_);
+		cs_soldier_capacity_->set_value(ts->desired_capacity);
+	} else if (upcast(Widelands::MilitarysiteSettings, ms, construction_site->get_settings())) {
+		assert(cs_soldier_capacity_);
+		assert(cs_prefer_heroes_);
+		cs_soldier_capacity_->set_value(ms->desired_capacity);
+		cs_prefer_heroes_->set_state(ms->prefer_heroes);
+	} else if (upcast(Widelands::WarehouseSettings, ws, construction_site->get_settings())) {
+		if (cs_launch_expedition_) {
+			cs_launch_expedition_->set_state(ws->launch_expedition);
+		}
+#ifndef NDEBUG
+		else {
+			assert(!ws->launch_expedition);
+		}
+#endif
+	}
 }

=== modified file 'src/wui/constructionsitewindow.h'
--- src/wui/constructionsitewindow.h	2019-02-23 11:00:49 +0000
+++ src/wui/constructionsitewindow.h	2019-05-14 17:38:23 +0000
@@ -20,8 +20,14 @@
 #ifndef WL_WUI_CONSTRUCTIONSITEWINDOW_H
 #define WL_WUI_CONSTRUCTIONSITEWINDOW_H
 
+#include <vector>
+
 #include "logic/map_objects/tribes/constructionsite.h"
+#include "ui_basic/button.h"
+#include "ui_basic/checkbox.h"
 #include "ui_basic/progressbar.h"
+#include "ui_basic/spinbox.h"
+#include "ui_basic/tabpanel.h"
 #include "wui/buildingwindow.h"
 
 /**
@@ -40,8 +46,72 @@
 	void init(bool avoid_fastclick, bool workarea_preview_wanted) override;
 
 private:
+	struct FakeInputQueue : UI::Box {
+		FakeInputQueue(Panel* parent,
+			           int32_t x,
+			           int32_t y,
+			           bool can_act,
+			           Widelands::ConstructionSite& cs,
+			           Widelands::WareWorker ww,
+			           Widelands::DescriptionIndex di);
+
+		void draw(RenderTarget& dst) override;
+		void think() override;
+
+		const Widelands::ProductionsiteSettings::InputQueueSetting& get_settings() const;
+
+	private:
+		Widelands::ConstructionSite& constructionsite_;
+		Widelands::ProductionsiteSettings& settings_;
+		Widelands::WareWorker type_;
+		Widelands::DescriptionIndex index_;
+
+		void change_fill(bool);
+		void change_priority(int32_t);
+
+		uint32_t max_fill_;
+		const Image* icon_;
+		const Image* max_fill_indicator_;
+
+		UI::Button* priority_high_;
+		UI::Button* priority_normal_;
+		UI::Button* priority_low_;
+	};
+
+	class FakeWaresDisplay : public WaresDisplay {
+	public:
+		FakeWaresDisplay(UI::Panel* parent,
+		                  bool can_act,
+		                  Widelands::ConstructionSite& cs,
+		                  Widelands::WareWorker type);
+
+	protected:
+		void draw_ware(RenderTarget& dst, Widelands::DescriptionIndex ware) override;
+
+	private:
+		Widelands::WarehouseSettings& settings_;
+	};
+
 	Widelands::OPtr<Widelands::ConstructionSite> construction_site_;
 	UI::ProgressBar* progress_;
+
+	// BuildingSettings-related UI elements
+	UI::Button* cs_enhance_;
+	UI::Checkbox* cs_launch_expedition_;
+	UI::Checkbox* cs_prefer_heroes_;
+	UI::SpinBox* cs_soldier_capacity_;
+	std::vector<FakeInputQueue*> cs_ware_queues_;
+	std::vector<FakeInputQueue*> cs_worker_queues_;
+	UI::Checkbox* cs_stopped_;
+	FakeWaresDisplay* cs_warehouse_wares_;
+	FakeWaresDisplay* cs_warehouse_workers_;
+	UI::Button* cs_warehouse_stock_policy_normal_;
+	UI::Button* cs_warehouse_stock_policy_prefer_;
+	UI::Button* cs_warehouse_stock_policy_dontstock_;
+	UI::Button* cs_warehouse_stock_policy_remove_;
+	UI::TabPanel* cs_warehouse_tabs_;
+	void change_policy(Widelands::StockPolicy);
+
 	DISALLOW_COPY_AND_ASSIGN(ConstructionSiteWindow);
 };
 

=== modified file 'src/wui/inputqueuedisplay.cc'
--- src/wui/inputqueuedisplay.cc	2019-04-25 06:40:24 +0000
+++ src/wui/inputqueuedisplay.cc	2019-05-14 17:38:23 +0000
@@ -205,13 +205,13 @@
 
 	int32_t priority = building_.get_priority(type_, index_, false);
 	switch (priority) {
-	case HIGH_PRIORITY:
+	case Widelands::kPriorityHigh:
 		priority_radiogroup_->set_state(0);
 		break;
-	case DEFAULT_PRIORITY:
+	case Widelands::kPriorityNormal:
 		priority_radiogroup_->set_state(1);
 		break;
-	case LOW_PRIORITY:
+	case Widelands::kPriorityLow:
 		priority_radiogroup_->set_state(2);
 		break;
 	default:
@@ -294,13 +294,13 @@
 
 	switch (state) {
 	case 0:
-		priority = HIGH_PRIORITY;
+		priority = Widelands::kPriorityHigh;
 		break;
 	case 1:
-		priority = DEFAULT_PRIORITY;
+		priority = Widelands::kPriorityNormal;
 		break;
 	case 2:
-		priority = LOW_PRIORITY;
+		priority = Widelands::kPriorityLow;
 		break;
 	default:
 		return;

=== modified file 'src/wui/warehousewindow.cc'
--- src/wui/warehousewindow.cc	2019-02-23 11:00:49 +0000
+++ src/wui/warehousewindow.cc	2019-05-14 17:38:23 +0000
@@ -75,19 +75,19 @@
 void WarehouseWaresDisplay::draw_ware(RenderTarget& dst, Widelands::DescriptionIndex ware) {
 	WaresDisplay::draw_ware(dst, ware);
 
-	Widelands::Warehouse::StockPolicy policy = warehouse_.get_stock_policy(get_type(), ware);
+	Widelands::StockPolicy policy = warehouse_.get_stock_policy(get_type(), ware);
 	const Image* pic = nullptr;
 	switch (policy) {
-	case Widelands::Warehouse::StockPolicy::kPrefer:
+	case Widelands::StockPolicy::kPrefer:
 		pic = g_gr->images().get(pic_policy_prefer);
 		break;
-	case Widelands::Warehouse::StockPolicy::kDontStock:
+	case Widelands::StockPolicy::kDontStock:
 		pic = g_gr->images().get(pic_policy_dontstock);
 		break;
-	case Widelands::Warehouse::StockPolicy::kRemove:
+	case Widelands::StockPolicy::kRemove:
 		pic = g_gr->images().get(pic_policy_remove);
 		break;
-	case Widelands::Warehouse::StockPolicy::kNormal:
+	case Widelands::StockPolicy::kNormal:
 		// don't draw anything for the normal policy
 		return;
 	}
@@ -106,7 +106,7 @@
 	                    Widelands::Warehouse&,
 	                    Widelands::WareWorker type);
 
-	void set_policy(Widelands::Warehouse::StockPolicy);
+	void set_policy(Widelands::StockPolicy);
 
 private:
 	InteractiveGameBase& gb_;
@@ -139,7 +139,7 @@
 	   buttons, #policy, 0, 0, 34, 34, UI::ButtonStyle::kWuiMenu,                                   \
 	   g_gr->images().get("images/wui/buildings/stock_policy_button_" #policy ".png"), tooltip),    \
 	b->sigclicked.connect(boost::bind(                                                              \
-	   &WarehouseWaresPanel::set_policy, this, Widelands::Warehouse::StockPolicy::k##policyname)),  \
+	   &WarehouseWaresPanel::set_policy, this, Widelands::StockPolicy::k##policyname)),  \
 	buttons->add(b);
 
 		ADD_POLICY_BUTTON(normal, Normal, _("Normal policy"))
@@ -152,7 +152,7 @@
 /**
  * Add Buttons policy buttons
  */
-void WarehouseWaresPanel::set_policy(Widelands::Warehouse::StockPolicy newpolicy) {
+void WarehouseWaresPanel::set_policy(Widelands::StockPolicy newpolicy) {
 	if (gb_.can_act(wh_.owner().player_number())) {
 		bool is_workers = type_ == Widelands::wwWORKER;
 		const std::set<Widelands::DescriptionIndex> indices =


Follow ups