← Back to team overview

widelands-dev team mailing list archive

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

 

Review: Needs Fixing

I finally got around to doing the code review. This branch still has a lot of room for streamlining the code and the performance.

Diff comments:

> 
> === 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-28 15:09:01 +0000
> @@ -0,0 +1,346 @@
> +/*
> + * 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) {

Why not simply use 

    for (const auto& WareAmount amount : descr.input_wares()) {
    }

And get rid of the WareRange struct?

> +		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);

Use WareDescr::has_demand_check to not add wares like log, wheat.

> +	}
> +	for (const DescriptionIndex di : tribe.workers()) {
> +		worker_preferences.emplace(di, StockPolicy::kNormal);

If you use WorkerDescr::has_demand_check() here, you can get rid of the loop below where you erase them.

> +	}
> +	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) {

These 2 data structures should have the same keys always - so you should be able to get the element from the second map directly and get rid of the inner loop. Same below for the workers.

> +					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;
> +				}
> +			}
> +			if (!result) {
> +				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
> 
> === modified file 'src/logic/map_objects/tribes/constructionsite.cc'
> --- src/logic/map_objects/tribes/constructionsite.cc	2019-05-26 17:21:15 +0000
> +++ src/logic/map_objects/tribes/constructionsite.cc	2019-05-28 15:09:01 +0000
> @@ -46,32 +51,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", nullptr) :
> -	                             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", nullptr) :
> +				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");

Basically the same code as for the intermediates. We could have a local lambda function here to get rid of the code duplication.

> +		const uint32_t anim_idx = known ?
> +				becomes->get_animation("build", nullptr) :
> +				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 - 1) : 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);
>  }
>  
>  /**
> @@ -207,6 +268,52 @@
>  			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());
> +					if (ts->desired_capacity != b.mutable_soldier_control()->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());
> +				if (ms->desired_capacity != b.mutable_soldier_control()->soldier_capacity()) {

You don't need the mutable in this line, only in the line below.

> +					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));
>  	}
> @@ -214,6 +321,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) {

I don't see any iterator manipulation here, so you can use a range-based for loop, which is easier to read.

> +		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();

I don't see any iterator manipulation here, so you can use a range-based for loop, which is easier to read.

> +
> +	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) {

Investigate whether you can get rid of the 4 double-loops in this function too (see above). No is a perfectly good answer ;)

> +					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)) {

Is there a case where the old settings would not be ProductionSite settings? If not, a dynamic_cast followed by an assert would ensure the correct behaviour.

> +			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.
>  ===============
>  */
> 
> === modified file 'src/logic/map_objects/tribes/militarysite.cc'
> --- src/logic/map_objects/tribes/militarysite.cc	2019-05-26 17:21:15 +0000
> +++ src/logic/map_objects/tribes/militarysite.cc	2019-05-28 15:09:01 +0000
> @@ -982,6 +982,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;

In trunk, we prefer rookies for small buildings like the Barbarian Sentry.

> +	return settings;
> +}
> +
>  // setters
>  
>  void MilitarySite::set_soldier_preference(SoldierPreference p) {
> 
> === 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-28 15:09:01 +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_;

Everything from here below is identical to ProdutionSite. Find a common spot where to parse this so that we can get rid of the code duplication.

> +	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/playercommand.cc'
> --- src/logic/playercommand.cc	2019-05-25 10:54:30 +0000
> +++ src/logic/playercommand.cc	2019-05-28 15:09:01 +0000
> @@ -1906,4 +1930,599 @@
>  	// 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_))) {

Can these not all be handled by the existing player commands like CmdChangeSoldierCapacity, by detecting there whether it's a construction site or not?

> +			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();
> +				}
> +				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();
> +				}
> +				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/wui/constructionsitewindow.cc'
> --- src/wui/constructionsitewindow.cc	2019-02-23 11:00:49 +0000
> +++ src/wui/constructionsitewindow.cc	2019-05-28 15:09:01 +0000
> @@ -22,9 +22,270 @@
>  #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_settings[] = "images/wui/menus/menu_stock.png";
> +static const char pic_tab_settings_wares[] = "images/wui/stats/menu_tab_wares_warehouse.png";
> +static const char pic_tab_settings_workers[] = "images/wui/stats/menu_tab_workers_warehouse.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";
> +static const char pic_decrease_capacity[] = "images/wui/buildings/menu_down_train.png";
> +static const char pic_increase_capacity[] = "images/wui/buildings/menu_up_train.png";
> +
> +constexpr uint32_t kPriorityButtonSize = 10;
> +constexpr uint32_t kFakeInputQueueWareWidth = WARE_MENU_PIC_WIDTH + 2;
> +constexpr uint32_t kFakeInputQueueWareHeight = 3 * kPriorityButtonSize;
> +constexpr uint32_t kFakeInputQueueButtonSize = 24;
> +
> +ConstructionSiteWindow::FakeInputQueue::FakeInputQueue(Panel* parent,

Create a common superclass for this and the real InputQueue to get rid of code duplication

> +               int32_t x,
> +               int32_t y,
> +               bool can_act,
> +               Widelands::ConstructionSite& cs,
> +               Widelands::WareWorker ww,
> +               Widelands::DescriptionIndex di)
> +		: UI::Panel(parent, x, y, 0, 0),
> +		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)),
> +		priority_group_(nullptr) {
> +	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, (kFakeInputQueueWareHeight - kFakeInputQueueButtonSize) / 2,
> +			kFakeInputQueueButtonSize, kFakeInputQueueButtonSize,
> +			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", kFakeInputQueueButtonSize + kFakeInputQueueWareWidth * max_fill_ + 8,
> +			(kFakeInputQueueWareHeight - kFakeInputQueueButtonSize) / 2,
> +			kFakeInputQueueButtonSize, kFakeInputQueueButtonSize, 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);
> +	if (type_ == Widelands::wwWARE) {
> +		priority_group_.reset(new UI::Radiogroup());
> +		Vector2i pos(kFakeInputQueueWareWidth * max_fill_ + 2 * kFakeInputQueueButtonSize + 10, 0);
> +		priority_group_->add_button(
> +		   this, pos, g_gr->images().get(pic_priority_high), _("Highest priority"));
> +		pos.y += kPriorityButtonSize;
> +		priority_group_->add_button(
> +		   this, pos, g_gr->images().get(pic_priority_normal), _("Normal priority"));
> +		pos.y += kPriorityButtonSize;
> +		priority_group_->add_button(
> +		   this, pos, g_gr->images().get(pic_priority_low), _("Lowest priority"));
> +		if (can_act) {
> +			priority_group_->changedto.connect([this](uint32_t state) {
> +				Widelands::Game& game = dynamic_cast<Widelands::Game&>(constructionsite_.get_owner()->egbase());
> +				int32_t priority;
> +				switch (state) {
> +					case 0:
> +						priority = Widelands::kPriorityHigh;
> +						break;
> +					case 1:
> +						priority = Widelands::kPriorityNormal;
> +						break;
> +					case 2:
> +						priority = Widelands::kPriorityLow;
> +						break;
> +					default:
> +						return;
> +				}
> +				if (SDL_GetModState() & KMOD_CTRL) {
> +					for (const auto& pair : settings_.ware_queues) {
> +						game.send_player_constructionsite_input_queue_priority(constructionsite_,
> +								Widelands::wwWARE, pair.first, priority);
> +					}
> +					for (const auto& pair : settings_.worker_queues) {
> +						game.send_player_constructionsite_input_queue_priority(constructionsite_,
> +								Widelands::wwWORKER, pair.first, priority);
> +					}
> +				} else {
> +					game.send_player_constructionsite_input_queue_priority(constructionsite_,
> +								type_, index_, priority);
> +				}
> +			});
> +		}
> +	}
> +	decrease.set_enabled(can_act);
> +	increase.set_enabled(can_act);
> +	update_desired_size();
> +}
> +
> +void ConstructionSiteWindow::FakeInputQueue::update_desired_size() {
> +	set_desired_size(kFakeInputQueueWareWidth * max_fill_ + 2 * kFakeInputQueueButtonSize + kPriorityButtonSize + 12,
> +			3 * kPriorityButtonSize);
> +}
> +
> +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();
> +	}
> +	NEVER_HERE();
> +}
> +
> +void ConstructionSiteWindow::FakeInputQueue::think() {
> +	UI::Panel::think();
> +	if (priority_group_) {
> +		switch (get_settings().priority) {
> +			case Widelands::kPriorityHigh:
> +				priority_group_->set_state(0);
> +				break;
> +			case Widelands::kPriorityNormal:
> +				priority_group_->set_state(1);
> +				break;
> +			case Widelands::kPriorityLow:
> +				priority_group_->set_state(2);
> +				break;
> +			default:
> +				NEVER_HERE();
> +		}
> +	}
> +}
> +
> +void ConstructionSiteWindow::FakeInputQueue::draw(RenderTarget& dst) {
> +	UI::Panel::draw(dst);
> +
> +	Vector2i point = Vector2i::zero();
> +	point.x = kFakeInputQueueButtonSize + 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,


-- 
https://code.launchpad.net/~widelands-dev/widelands/constructionsite_options/+merge/367428
Your team Widelands Developers is subscribed to branch lp:~widelands-dev/widelands/constructionsite_options.


References