← Back to team overview

widelands-dev team mailing list archive

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

 

SirVer has proposed merging lp:~widelands-dev/widelands/reduce_overlay_manager_use2 into lp:widelands.

Commit message:
Further reduce the use of the FieldOverlayManager.

- Draw field selectors in the draw routine directly, not using the FieldOverlayManager.
- Draw work area preview markers in the draw routine directly, not using the FieldOverlayManager.


Requested reviews:
  Widelands Developers (widelands-dev)
Related bugs:
  Bug #1711394 in widelands: "Terrain tool no longer displays the selection on the triangles, but on the nodes"
  https://bugs.launchpad.net/widelands/+bug/1711394

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/reduce_overlay_manager_use2/+merge/330125
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/reduce_overlay_manager_use2 into lp:widelands.
=== modified file 'src/editor/editorinteractive.cc'
--- src/editor/editorinteractive.cc	2017-09-02 19:34:45 +0000
+++ src/editor/editorinteractive.cc	2017-09-03 13:04:48 +0000
@@ -43,6 +43,7 @@
 #include "logic/map_objects/tribes/tribes.h"
 #include "logic/map_objects/world/resource_description.h"
 #include "logic/map_objects/world/world.h"
+#include "logic/maptriangleregion.h"
 #include "logic/player.h"
 #include "map_io/map_loader.h"
 #include "map_io/widelands_map_loader.h"
@@ -274,6 +275,22 @@
 		starting_positions[map.get_starting_pos(i)] = i;
 	}
 
+	// Figure out which fields are currently under the selection.
+	std::set<Widelands::Coords> selected_nodes;
+	std::set<Widelands::TCoords<>> selected_triangles;
+	if (!get_sel_triangles()) {
+		Widelands::MapRegion<> mr(map, Widelands::Area<>(get_sel_pos().node, get_sel_radius()));
+		do {
+			selected_nodes.emplace(mr.location());
+		} while (mr.advance(map));
+	} else {
+		Widelands::MapTriangleRegion<> mr(
+		   map, Widelands::Area<Widelands::TCoords<>>(get_sel_pos().triangle, get_sel_radius()));
+		do {
+			selected_triangles.emplace(mr.location());
+		} while (mr.advance(map));
+	}
+
 	const auto& world = ebase.world();
 	for (size_t idx = 0; idx < fields_to_draw->size(); ++idx) {
 		const FieldsToDraw::Field& field = fields_to_draw->at(idx);
@@ -291,12 +308,17 @@
 			}
 		}
 
-		const auto blit_overlay = [&dst, &field, scale](const Image* pic, const Vector2i& hotspot) {
-			dst.blitrect_scale(Rectf(field.rendertarget_pixel - hotspot.cast<float>() * scale,
+		const auto blit = [&dst, &field, scale](
+		   const Image* pic, const Vector2f& position, const Vector2i& hotspot) {
+			dst.blitrect_scale(Rectf(position - hotspot.cast<float>() * scale,
 			                         pic->width() * scale, pic->height() * scale),
 			                   pic, Recti(0, 0, pic->width(), pic->height()), 1.f,
 			                   BlendMode::UseAlpha);
 		};
+		const auto blit_overlay = [&dst, &field, scale, &blit](
+		   const Image* pic, const Vector2i& hotspot) {
+			blit(pic, field.rendertarget_pixel, hotspot);
+		};
 
 		// Draw resource overlay.
 		uint8_t const amount = field.fcoords.field->get_resources_amount();
@@ -322,6 +344,39 @@
 			constexpr int kStartingPosHotspotY = 55;
 			blit_overlay(player_image, Vector2i(player_image->width() / 2, kStartingPosHotspotY));
 		}
+
+		// Draw selection markers on the field.
+		if (selected_nodes.count(field.fcoords) > 0) {
+			const Image* pic = get_sel_picture();
+			blit_overlay(pic, Vector2i(pic->width() / 2, pic->height() / 2));
+		}
+
+		// Draw selection markers on the triangles.
+		if (field.all_neighbors_valid()) {
+			const FieldsToDraw::Field& rn = fields_to_draw->at(field.rn_index);
+			const FieldsToDraw::Field& brn = fields_to_draw->at(field.brn_index);
+			const FieldsToDraw::Field& bln = fields_to_draw->at(field.bln_index);
+			if (selected_triangles.count(
+			       Widelands::TCoords<>(field.fcoords, Widelands::TriangleIndex::R))) {
+				const Vector2f tripos(
+				   (field.rendertarget_pixel.x + rn.rendertarget_pixel.x + brn.rendertarget_pixel.x) /
+				      3.f,
+				   (field.rendertarget_pixel.y + rn.rendertarget_pixel.y + brn.rendertarget_pixel.y) /
+				      3.f);
+				const Image* pic = get_sel_picture();
+				blit(pic, tripos, Vector2i(pic->width() / 2, pic->height() / 2));
+			}
+			if (selected_triangles.count(
+			       Widelands::TCoords<>(field.fcoords, Widelands::TriangleIndex::D))) {
+				const Vector2f tripos(
+				   (field.rendertarget_pixel.x + bln.rendertarget_pixel.x + brn.rendertarget_pixel.x) /
+				      3.f,
+				   (field.rendertarget_pixel.y + bln.rendertarget_pixel.y + brn.rendertarget_pixel.y) /
+				      3.f);
+				const Image* pic = get_sel_picture();
+				blit(pic, tripos, Vector2i(pic->width() / 2, pic->height() / 2));
+			}
+		}
 	}
 }
 

=== modified file 'src/logic/map_objects/tribes/building.h'
--- src/logic/map_objects/tribes/building.h	2017-08-09 17:55:34 +0000
+++ src/logic/map_objects/tribes/building.h	2017-09-03 13:04:48 +0000
@@ -155,6 +155,10 @@
 	virtual uint32_t get_conquers() const;
 	virtual uint32_t vision_range() const;
 
+	const WorkareaInfo& workarea_info() const { return workarea_info_; }
+
+	// TODO(sirver): This should not be public. It is mutated by other classes
+	// in many places.
 	WorkareaInfo workarea_info_;
 
 	bool suitability(const Map&, const FCoords&) const;

=== modified file 'src/logic/map_objects/tribes/workarea_info.h'
--- src/logic/map_objects/tribes/workarea_info.h	2017-01-25 18:55:59 +0000
+++ src/logic/map_objects/tribes/workarea_info.h	2017-09-03 13:04:48 +0000
@@ -39,11 +39,8 @@
  * See LuaBuildingDescription::get_workarea_radius, InteractiveBase::show_work_area
  */
 
-// TODO(Hasi50): LuaBuildingDescription::get_workarea_radius will only give us the very first
-// size found, which seems to be correct but depends on the std:map implementation
-//
-// We could just use a unit8 as base for the map?
-// We should document (as const) the expected stings.
+// TODO(Hasi50): We could just use a unit8 as base for the map? We should
+// document (as const) the expected stings.
 
 using WorkareaInfo = std::map<uint32_t, std::set<std::string>>;
 

=== modified file 'src/logic/maptriangleregion.cc'
--- src/logic/maptriangleregion.cc	2017-08-31 10:55:45 +0000
+++ src/logic/maptriangleregion.cc	2017-09-03 13:04:48 +0000
@@ -144,7 +144,6 @@
 template <>
 MapTriangleRegion<TCoords<FCoords>>::MapTriangleRegion(const Map& map, Area<TCoords<FCoords>> area)
    : radius_is_odd_(area.radius & 1), location_(area) {
-	assert(area.t == TriangleIndex::R || area.t == TriangleIndex::D);
 	const uint16_t radius_plus_1 = area.radius + 1;
 	const uint16_t half_radius_rounded_down = area.radius / 2;
 	row_length_ = radius_plus_1;

=== modified file 'src/logic/widelands_geometry.h'
--- src/logic/widelands_geometry.h	2017-09-02 19:51:07 +0000
+++ src/logic/widelands_geometry.h	2017-09-03 13:04:48 +0000
@@ -126,6 +126,9 @@
 	bool operator!=(const TCoords& other) const {
 		return !(*this == other);
 	}
+	bool operator<(const TCoords& other) const {
+		return std::forward_as_tuple(node, t) < std::forward_as_tuple(other.node, other.t);
+	}
 
 	CoordsType node;
 	TriangleIndex t;

=== modified file 'src/scripting/lua_map.cc'
--- src/scripting/lua_map.cc	2017-08-31 15:22:21 +0000
+++ src/scripting/lua_map.cc	2017-09-03 13:04:48 +0000
@@ -2133,7 +2133,7 @@
               nil in case bulding has no workareas
 */
 int LuaBuildingDescription::get_workarea_radius(lua_State* L) {
-	const WorkareaInfo& workareaInfo = get()->workarea_info_;
+	const WorkareaInfo& workareaInfo = get()->workarea_info();
 	if (!workareaInfo.empty()) {
 		lua_pushinteger(L, workareaInfo.begin()->first);
 	} else {

=== modified file 'src/wui/buildingwindow.cc'
--- src/wui/buildingwindow.cc	2017-08-17 15:34:45 +0000
+++ src/wui/buildingwindow.cc	2017-09-03 13:04:48 +0000
@@ -53,7 +53,8 @@
      is_dying_(false),
      parent_(&parent),
      building_(b),
-     workarea_overlay_id_(0),
+	  building_position_(b.get_position()),
+     showing_workarea_(false),
      avoid_fastclick_(avoid_fastclick),
      expeditionbtn_(nullptr) {
 	buildingnotes_subscriber_ = Notifications::subscribe<Widelands::NoteBuilding>(
@@ -61,9 +62,7 @@
 }
 
 BuildingWindow::~BuildingWindow() {
-	if (workarea_overlay_id_) {
-		igbase()->mutable_field_overlay_manager()->remove_overlay(workarea_overlay_id_);
-	}
+	hide_workarea();
 }
 
 void BuildingWindow::on_building_note(const Widelands::NoteBuilding& note) {
@@ -77,7 +76,7 @@
 			break;
 		// The building is no more
 		case Widelands::NoteBuilding::Action::kStartWarp:
-			igbase()->add_wanted_building_window(building().get_position(), get_pos(), is_minimal());
+			igbase()->add_wanted_building_window(building_position_, get_pos(), is_minimal());
 			FALLS_THROUGH;
 		case Widelands::NoteBuilding::Action::kDeleted:
 			die();
@@ -116,6 +115,7 @@
 // Stop everybody from thinking to avoid segfaults
 void BuildingWindow::die() {
 	is_dying_ = true;
+	hide_workarea();
 	set_thinks(false);
 	vbox_.reset(nullptr);
 	UniqueWindow::die();
@@ -287,13 +287,13 @@
 	}
 
 	if (can_see) {
-		WorkareaInfo wa_info;
+		const WorkareaInfo* wa_info;
 		if (upcast(Widelands::ConstructionSite, csite, &building_)) {
-			wa_info = csite->building().workarea_info_;
+			wa_info = &csite->building().workarea_info();
 		} else {
-			wa_info = building_.descr().workarea_info_;
+			wa_info = &building_.descr().workarea_info();
 		}
-		if (!wa_info.empty()) {
+		if (!wa_info->empty()) {
 			toggle_workarea_ = new UI::Button(
 			   capsbuttons, "workarea", 0, 0, 34, 34, g_gr->images().get("images/ui_basic/but4.png"),
 			   g_gr->images().get("images/wui/overlays/workarea123.png"), _("Hide work area"));
@@ -419,26 +419,27 @@
 ===============
 */
 void BuildingWindow::act_debug() {
-	show_field_debug(*igbase(), igbase()->game().map().get_fcoords(building_.get_position()));
+	show_field_debug(*igbase(), igbase()->game().map().get_fcoords(building_position_));
 }
 
 /**
  * Show the building's workarea (if it has one).
  */
 void BuildingWindow::show_workarea() {
-	if (workarea_overlay_id_) {
+	if (showing_workarea_) {
 		return;  // already shown, nothing to be done
 	}
-	WorkareaInfo workarea_info;
+	const WorkareaInfo* workarea_info;
 	if (upcast(Widelands::ConstructionSite, csite, &building_)) {
-		workarea_info = csite->building().workarea_info_;
+		workarea_info = &csite->building().workarea_info();
 	} else {
-		workarea_info = building_.descr().workarea_info_;
+		workarea_info = &building_.descr().workarea_info();
 	}
-	if (workarea_info.empty()) {
+	if (workarea_info->empty()) {
 		return;
 	}
-	workarea_overlay_id_ = igbase()->show_work_area(workarea_info, building_.get_position());
+	igbase()->show_work_area(*workarea_info, building_position_);
+	showing_workarea_ = true;
 
 	configure_workarea_button();
 }
@@ -447,10 +448,9 @@
  * Hide the workarea from view.
  */
 void BuildingWindow::hide_workarea() {
-	if (workarea_overlay_id_) {
-		igbase()->hide_work_area(workarea_overlay_id_);
-		workarea_overlay_id_ = 0;
-
+	if (showing_workarea_) {
+		igbase()->hide_work_area(building_position_);
+		showing_workarea_ = false;
 		configure_workarea_button();
 	}
 }
@@ -460,7 +460,7 @@
  */
 void BuildingWindow::configure_workarea_button() {
 	if (toggle_workarea_) {
-		if (workarea_overlay_id_) {
+		if (showing_workarea_) {
 			toggle_workarea_->set_tooltip(_("Hide work area"));
 			toggle_workarea_->set_perm_pressed(true);
 		} else {
@@ -471,7 +471,7 @@
 }
 
 void BuildingWindow::toggle_workarea() {
-	if (workarea_overlay_id_) {
+	if (showing_workarea_) {
 		hide_workarea();
 	} else {
 		show_workarea();
@@ -491,7 +491,7 @@
  * for the corresponding button.
  */
 void BuildingWindow::clicked_goto() {
-	igbase()->map_view()->scroll_to_field(building().get_position(), MapView::Transition::Smooth);
+	igbase()->map_view()->scroll_to_field(building_position_, MapView::Transition::Smooth);
 }
 
 void BuildingWindow::update_expedition_button(bool expedition_was_canceled) {

=== modified file 'src/wui/buildingwindow.h'
--- src/wui/buildingwindow.h	2017-06-06 05:37:57 +0000
+++ src/wui/buildingwindow.h	2017-09-03 13:04:48 +0000
@@ -90,11 +90,21 @@
 	bool is_dying_;
 
 private:
+	// Actions performed when a NoteBuilding is received.
+	void on_building_note(const Widelands::NoteBuilding& note);
+
+	// For ports only.
+	void update_expedition_button(bool expedition_was_canceled);
+
 	InteractiveGameBase* parent_;
-	/// Actions performed when a NoteBuilding is received.
-	void on_building_note(const Widelands::NoteBuilding& note);
+
 	Widelands::Building& building_;
 
+	// We require this to unregister overlays when we are closed. Since the
+	// building might have been destroyed by then we have to keep a copy of its
+	// position around.
+	const Widelands::Coords building_position_;
+
 	std::unique_ptr<UI::Box> vbox_;
 
 	UI::TabPanel* tabs_;
@@ -107,12 +117,9 @@
 	Widelands::PlayerNumber capscache_player_number_;
 	bool caps_setup_;
 
-	FieldOverlayManager::OverlayId workarea_overlay_id_;
+	bool showing_workarea_;
 	bool avoid_fastclick_;
 
-	// For ports only.
-	void update_expedition_button(bool expedition_was_canceled);
-
 	UI::Button* expeditionbtn_;
 	std::unique_ptr<Notifications::Subscriber<Widelands::NoteExpeditionCanceled>>
 	   expedition_canceled_subscriber_;

=== modified file 'src/wui/field_overlay_manager.h'
--- src/wui/field_overlay_manager.h	2017-08-28 13:37:51 +0000
+++ src/wui/field_overlay_manager.h	2017-09-03 13:04:48 +0000
@@ -53,7 +53,6 @@
 // drawn below the buildhelp, everything higher above.
 enum class OverlayLevel {
 	kWorkAreaPreview = 0,
-	kSelection = 7,
 	kRoadBuildSlope = 8,
 };
 

=== modified file 'src/wui/fieldaction.cc'
--- src/wui/fieldaction.cc	2017-08-30 10:29:46 +0000
+++ src/wui/fieldaction.cc	2017-09-03 13:04:48 +0000
@@ -196,14 +196,13 @@
 
 	Widelands::Player* player_;
 	const Widelands::Map& map_;
-	FieldOverlayManager& field_overlay_manager_;
 
 	Widelands::FCoords node_;
 
 	UI::TabPanel tabpanel_;
 	bool fastclick_;  // if true, put the mouse over first button in first tab
 	uint32_t best_tab_;
-	FieldOverlayManager::OverlayId workarea_preview_overlay_id_;
+	bool showing_workarea_preview_;
 
 	/// Variables to use with attack dialog.
 	AttackBox* attack_box_;
@@ -247,20 +246,19 @@
    : UI::UniqueWindow(ib, "field_action", registry, 68, 34, _("Action")),
      player_(plr),
      map_(ib->egbase().map()),
-     field_overlay_manager_(*ib->mutable_field_overlay_manager()),
      node_(ib->get_sel_pos().node, &map_[ib->get_sel_pos().node]),
      tabpanel_(this, g_gr->images().get("images/ui_basic/but1.png")),
      fastclick_(true),
      best_tab_(0),
-     workarea_preview_overlay_id_(0),
+     showing_workarea_preview_(false),
      attack_box_(nullptr) {
 	ib->set_sel_freeze(true);
 	set_center_panel(&tabpanel_);
 }
 
 FieldActionWindow::~FieldActionWindow() {
-	if (workarea_preview_overlay_id_)
-		field_overlay_manager_.remove_overlay(workarea_preview_overlay_id_);
+	if (showing_workarea_preview_)
+		ibase().hide_work_area(node_);
 	ibase().set_sel_freeze(false);
 	delete attack_box_;
 }
@@ -685,17 +683,18 @@
 }
 
 void FieldActionWindow::building_icon_mouse_out(Widelands::DescriptionIndex) {
-	if (workarea_preview_overlay_id_) {
-		field_overlay_manager_.remove_overlay(workarea_preview_overlay_id_);
-		workarea_preview_overlay_id_ = 0;
+	if (showing_workarea_preview_) {
+		ibase().hide_work_area(node_);
+		showing_workarea_preview_ = false;
 	}
 }
 
 void FieldActionWindow::building_icon_mouse_in(const Widelands::DescriptionIndex idx) {
-	if (ibase().show_workarea_preview_ && !workarea_preview_overlay_id_) {
+	if (ibase().show_workarea_preview_ && !showing_workarea_preview_) {
 		const WorkareaInfo& workarea_info =
-		   player_->tribe().get_building_descr(Widelands::DescriptionIndex(idx))->workarea_info_;
-		workarea_preview_overlay_id_ = ibase().show_work_area(workarea_info, node_);
+		   player_->tribe().get_building_descr(Widelands::DescriptionIndex(idx))->workarea_info();
+		ibase().show_work_area(workarea_info, node_);
+		showing_workarea_preview_ = true;
 	}
 }
 

=== modified file 'src/wui/interactive_base.cc'
--- src/wui/interactive_base.cc	2017-09-02 19:36:19 +0000
+++ src/wui/interactive_base.cc	2017-09-03 13:04:48 +0000
@@ -151,44 +151,20 @@
 
 void InteractiveBase::set_sel_pos(Widelands::NodeAndTriangle<> const center) {
 	const Map& map = egbase().map();
-
-	// Remove old sel pointer
-	if (sel_.jobid)
-		field_overlay_manager_->remove_overlay(sel_.jobid);
-	const FieldOverlayManager::OverlayId jobid = sel_.jobid =
-	   field_overlay_manager_->next_overlay_id();
-
 	sel_.pos = center;
 
-	//  register sel overlay position
-	if (sel_.triangles) {
-		assert(center.triangle.t == Widelands::TriangleIndex::D ||
-		       center.triangle.t == Widelands::TriangleIndex::R);
-		Widelands::MapTriangleRegion<> mr(map, Area<TCoords<>>(center.triangle, sel_.radius));
-		do
-			field_overlay_manager_->register_overlay(
-			   mr.location().node, sel_.pic, OverlayLevel::kSelection, Vector2i::invalid(), jobid);
-		while (mr.advance(map));
-	} else {
-		Widelands::MapRegion<> mr(map, Area<>(center.node, sel_.radius));
-		do
-			field_overlay_manager_->register_overlay(
-			   mr.location(), sel_.pic, OverlayLevel::kSelection, Vector2i::invalid(), jobid);
-		while (mr.advance(map));
-		if (upcast(InteractiveGameBase const, igbase, this))
-			if (upcast(Widelands::ProductionSite, productionsite, map[center.node].get_immovable())) {
-				if (upcast(InteractivePlayer const, iplayer, igbase)) {
-					const Widelands::Player& player = iplayer->player();
-					if (!player.see_all() &&
-					    (1 >= player.vision(Widelands::Map::get_index(center.node, map.get_width())) ||
-					     player.is_hostile(*productionsite->get_owner())))
-						return set_tooltip("");
-				}
-				set_tooltip(
-				   productionsite->info_string(Widelands::Building::InfoStringFormat::kTooltip));
-				return;
+	if (upcast(InteractiveGameBase const, igbase, this))
+		if (upcast(Widelands::ProductionSite, productionsite, map[center.node].get_immovable())) {
+			if (upcast(InteractivePlayer const, iplayer, igbase)) {
+				const Widelands::Player& player = iplayer->player();
+				if (!player.see_all() &&
+				    (1 >= player.vision(Widelands::Map::get_index(center.node, map.get_width())) ||
+				     player.is_hostile(*productionsite->get_owner())))
+					return set_tooltip("");
 			}
-	}
+			set_tooltip(productionsite->info_string(Widelands::Building::InfoStringFormat::kTooltip));
+			return;
+		}
 	set_tooltip("");
 }
 
@@ -263,48 +239,52 @@
 }
 
 // Show the given workareas at the given coords and returns the overlay job id associated
-FieldOverlayManager::OverlayId InteractiveBase::show_work_area(const WorkareaInfo& workarea_info,
-                                                               Widelands::Coords coords) {
-	const uint8_t workareas_nrs = workarea_info.size();
-	WorkareaInfo::size_type wa_index;
-	switch (workareas_nrs) {
-	case 0:
-		return 0;  // no workarea
-	case 1:
-		wa_index = 5;
-		break;
-	case 2:
-		wa_index = 3;
-		break;
-	case 3:
-		wa_index = 0;
-		break;
-	default:
-		throw wexception("Encountered unexpected WorkareaInfo size %i", workareas_nrs);
-	}
-	const Widelands::Map& map = egbase_.map();
-	FieldOverlayManager::OverlayId overlay_id = field_overlay_manager_->next_overlay_id();
-
-	Widelands::HollowArea<> hollow_area(Widelands::Area<>(coords, 0), 0);
-
-	// Iterate through the work areas, from building to its enhancement
-	WorkareaInfo::const_iterator it = workarea_info.begin();
-	for (; it != workarea_info.end(); ++it) {
-		hollow_area.radius = it->first;
-		Widelands::MapHollowRegion<> mr(map, hollow_area);
-		do
-			field_overlay_manager_->register_overlay(mr.location(), workarea_pics_[wa_index],
-			                                         OverlayLevel::kWorkAreaPreview,
-			                                         Vector2i::invalid(), overlay_id);
-		while (mr.advance(map));
-		wa_index++;
-		hollow_area.hole_radius = hollow_area.radius;
-	}
-	return overlay_id;
-}
-
-void InteractiveBase::hide_work_area(FieldOverlayManager::OverlayId overlay_id) {
-	field_overlay_manager_->remove_overlay(overlay_id);
+void InteractiveBase::show_work_area(const WorkareaInfo& workarea_info, Widelands::Coords coords) {
+	work_area_previews_[coords] = &workarea_info;
+}
+
+std::map<Coords, const Image*> InteractiveBase::get_work_area_overlays(const Widelands::Map& map) const {
+	std::map<Coords, const Image*> result;
+	for (const auto& pair : work_area_previews_) {
+		const Coords& coords = pair.first;
+		const WorkareaInfo* workarea_info = pair.second;
+		WorkareaInfo::size_type wa_index;
+		switch (workarea_info->size()) {
+		case 0:
+			continue;  // no workarea
+		case 1:
+			wa_index = 5;
+			break;
+		case 2:
+			wa_index = 3;
+			break;
+		case 3:
+			wa_index = 0;
+			break;
+		default:
+			throw wexception(
+			   "Encountered unexpected WorkareaInfo size %i", static_cast<int>(workarea_info->size()));
+		}
+
+		Widelands::HollowArea<> hollow_area(Widelands::Area<>(coords, 0), 0);
+
+		// Iterate through the work areas, from building to its enhancement
+		WorkareaInfo::const_iterator it = workarea_info->begin();
+		for (; it != workarea_info->end(); ++it) {
+			hollow_area.radius = it->first;
+			Widelands::MapHollowRegion<> mr(map, hollow_area);
+			do {
+				result[mr.location()] = workarea_pics_[wa_index];
+			} while (mr.advance(map));
+			wa_index++;
+			hollow_area.hole_radius = hollow_area.radius;
+		}
+	}
+	return result;
+}
+
+void InteractiveBase::hide_work_area(const Widelands::Coords& coords) {
+	work_area_previews_.erase(coords);
 }
 
 /**

=== modified file 'src/wui/interactive_base.h'
--- src/wui/interactive_base.h	2017-09-02 19:36:19 +0000
+++ src/wui/interactive_base.h	2017-09-03 13:04:48 +0000
@@ -75,9 +75,9 @@
 
 	// TODO(sirver): This should be private.
 	bool show_workarea_preview_;
-	FieldOverlayManager::OverlayId show_work_area(const WorkareaInfo& workarea_info,
+	void show_work_area(const WorkareaInfo& workarea_info,
 	                                              Widelands::Coords coords);
-	void hide_work_area(FieldOverlayManager::OverlayId overlay_id);
+	void hide_work_area(const Widelands::Coords& coords);
 
 	//  point of view for drawing
 	virtual Widelands::Player* get_player() const = 0;
@@ -198,6 +198,7 @@
 
 	void unset_sel_picture();
 	void set_sel_picture(const Image* image);
+	const Image* get_sel_picture() { return sel_.pic; }
 	void adjust_toolbar_position() {
 		toolbar_.set_pos(Vector2i((get_inner_w() - toolbar_.get_w()) >> 1, get_inner_h() - 34));
 	}
@@ -213,6 +214,10 @@
 	// Returns the information which overlay text should currently be drawn.
 	TextToDraw get_text_to_draw() const;
 
+	// Returns the current overlays for the work area previews.
+	std::map<Widelands::Coords, const Image*>
+	get_work_area_overlays(const Widelands::Map& map) const;
+
 private:
 	int32_t stereo_position(Widelands::Coords position_map);
 	void resize_chat_overlay();
@@ -239,45 +244,49 @@
 		uint32_t radius;
 		const Image* pic;
 		FieldOverlayManager::OverlayId jobid;
-	} sel_;
-
-	MapView map_view_;
-	ChatOverlay* chat_overlay_;
-
-	// These get collected by add_toolbar_button
-	// so we can call unassign_toggle_button on them in the destructor.
-	std::vector<UI::UniqueWindow::Registry> registries_;
-
-	UI::Box toolbar_;
-	// No unique_ptr on purpose: 'minimap_' is a UniqueWindow, its parent will
-	// delete it.
-	MiniMap* minimap_;
-	MiniMap::Registry minimap_registry_;
-	QuickNavigation quick_navigation_;
-
-	std::unique_ptr<FieldOverlayManager> field_overlay_manager_;
-
-	// The roads that are displayed while a road is being build. They are not
-	// yet logically in the game, but need to be displayed for the user as
-	// visual guide. The data type is the same as for Field::road.
-	std::map<Widelands::Coords, uint8_t> road_building_preview_;
-
-	std::unique_ptr<Notifications::Subscriber<GraphicResolutionChanged>>
-	   graphic_resolution_changed_subscriber_;
-	std::unique_ptr<Notifications::Subscriber<NoteSound>> sound_subscriber_;
-	Widelands::EditorGameBase& egbase_;
-	uint32_t display_flags_;
-	uint32_t lastframe_;        //  system time (milliseconds)
-	uint32_t frametime_;        //  in millseconds
-	uint32_t avg_usframetime_;  //  in microseconds!
-
-	FieldOverlayManager::OverlayId road_buildhelp_overlay_jobid_;
-	Widelands::CoordPath* buildroad_;  //  path for the new road
-	Widelands::PlayerNumber road_build_player_;
-
-	UI::UniqueWindow::Registry debugconsole_;
-	std::unique_ptr<UniqueWindowHandler> unique_window_handler_;
-	std::vector<const Image*> workarea_pics_;
+		} sel_;
+
+		MapView map_view_;
+		ChatOverlay* chat_overlay_;
+
+		// These get collected by add_toolbar_button
+		// so we can call unassign_toggle_button on them in the destructor.
+		std::vector<UI::UniqueWindow::Registry> registries_;
+
+		UI::Box toolbar_;
+		// No unique_ptr on purpose: 'minimap_' is a UniqueWindow, its parent will
+		// delete it.
+		MiniMap* minimap_;
+		MiniMap::Registry minimap_registry_;
+		QuickNavigation quick_navigation_;
+
+		std::unique_ptr<FieldOverlayManager> field_overlay_manager_;
+
+		// The currently enabled work area previews. They are keyed by the
+		// coordinate that the building that shows the work area is positioned.
+	   std::map<Widelands::Coords, const WorkareaInfo*> work_area_previews_;
+
+	   // The roads that are displayed while a road is being build. They are not
+		// yet logically in the game, but need to be displayed for the user as
+		// visual guide. The data type is the same as for Field::road.
+		std::map<Widelands::Coords, uint8_t> road_building_preview_;
+
+		std::unique_ptr<Notifications::Subscriber<GraphicResolutionChanged>>
+		   graphic_resolution_changed_subscriber_;
+		std::unique_ptr<Notifications::Subscriber<NoteSound>> sound_subscriber_;
+		Widelands::EditorGameBase& egbase_;
+		uint32_t display_flags_;
+		uint32_t lastframe_;        //  system time (milliseconds)
+		uint32_t frametime_;        //  in millseconds
+		uint32_t avg_usframetime_;  //  in microseconds!
+
+		FieldOverlayManager::OverlayId road_buildhelp_overlay_jobid_;
+		Widelands::CoordPath* buildroad_;  //  path for the new road
+		Widelands::PlayerNumber road_build_player_;
+
+		UI::UniqueWindow::Registry debugconsole_;
+		std::unique_ptr<UniqueWindowHandler> unique_window_handler_;
+		std::vector<const Image*> workarea_pics_;
 };
 
 #endif  // end of include guard: WL_WUI_INTERACTIVE_BASE_H

=== modified file 'src/wui/interactive_player.cc'
--- src/wui/interactive_player.cc	2017-09-02 19:34:45 +0000
+++ src/wui/interactive_player.cc	2017-09-03 13:04:48 +0000
@@ -320,6 +320,10 @@
 }
 
 void InteractivePlayer::draw_map_view(MapView* given_map_view, RenderTarget* dst) {
+	// In-game, selection can never be on triangles or have a radius.
+	assert(get_sel_radius() == 0);
+	assert(!get_sel_triangles());
+
 	const Widelands::Player& plr = player();
 	const auto& gbase = egbase();
 	const Widelands::Map& map = gbase.map();
@@ -327,6 +331,7 @@
 
 	auto* fields_to_draw = given_map_view->draw_terrain(gbase, dst);
 	const auto& roads_preview = road_building_preview();
+	const std::map<Widelands::Coords, const Image*> work_area_overlays = get_work_area_overlays(map);
 
 	for (size_t idx = 0; idx < fields_to_draw->size(); ++idx) {
 		auto* f = fields_to_draw->mutable_field(idx);
@@ -349,9 +354,11 @@
 		}
 
 		// Add road building overlays if applicable.
-		const auto it = roads_preview.find(f->fcoords);
-		if (it != roads_preview.end()) {
-			f->roads |= it->second;
+		{
+			const auto it = roads_preview.find(f->fcoords);
+			if (it != roads_preview.end()) {
+				f->roads |= it->second;
+			}
 		}
 
 		const float scale = 1.f / given_map_view->view().zoom;
@@ -367,15 +374,30 @@
 			draw_immovables_for_formerly_visible_field(*f, player_field, scale, dst);
 		}
 
+		const auto blit_overlay = [dst, f, scale](const Image* pic, const Vector2i& hotspot) {
+			dst->blitrect_scale(Rectf(f->rendertarget_pixel - hotspot.cast<float>() * scale,
+			                          pic->width() * scale, pic->height() * scale),
+			                    pic, Recti(0, 0, pic->width(), pic->height()), 1.f,
+			                    BlendMode::UseAlpha);
+		};
+
+		// Draw work area previews.
+		{
+			const auto it = work_area_overlays.find(f->fcoords);
+			if (it != work_area_overlays.end()) {
+				blit_overlay(it->second, Vector2i(it->second->width() / 2, it->second->height() / 2));
+			}
+		}
+
 		// TODO(sirver): Do not use the field_overlay_manager, instead draw the
 		// overlays we are interested in here directly.
-		field_overlay_manager().foreach_overlay(
-		   f->fcoords, [dst, f, scale](const Image* pic, const Vector2i& hotspot) {
-			   dst->blitrect_scale(Rectf(f->rendertarget_pixel - hotspot.cast<float>() * scale,
-			                             pic->width() * scale, pic->height() * scale),
-			                       pic, Recti(0, 0, pic->width(), pic->height()), 1.f,
-			                       BlendMode::UseAlpha);
-			});
+		field_overlay_manager().foreach_overlay(f->fcoords, blit_overlay);
+
+		// Blit the selection marker.
+		if (f->fcoords == get_sel_pos().node) {
+			const Image* pic = get_sel_picture();
+			blit_overlay(pic, Vector2i(pic->width() / 2, pic->height() / 2));
+		}
 	}
 }
 

=== modified file 'src/wui/interactive_spectator.cc'
--- src/wui/interactive_spectator.cc	2017-08-28 12:44:08 +0000
+++ src/wui/interactive_spectator.cc	2017-09-03 13:04:48 +0000
@@ -111,6 +111,9 @@
 void InteractiveSpectator::draw_map_view(MapView* given_map_view, RenderTarget* dst) {
 	// A spectator cannot build roads.
 	assert(road_building_preview().empty());
+	// In-game, selection can never be on triangles or have a radius.
+	assert(get_sel_radius() == 0);
+	assert(!get_sel_triangles());
 
 	const auto& gbase = egbase();
 	auto* fields_to_draw = given_map_view->draw_terrain(gbase, dst);
@@ -118,6 +121,8 @@
 	const uint32_t gametime = gbase.get_gametime();
 
 	const auto text_to_draw = get_text_to_draw();
+	const std::map<Widelands::Coords, const Image*> work_area_overlays =
+	   get_work_area_overlays(gbase.map());
 	for (size_t idx = 0; idx < fields_to_draw->size(); ++idx) {
 		const FieldsToDraw::Field& field = fields_to_draw->at(idx);
 
@@ -133,15 +138,28 @@
 			bob->draw(gbase, text_to_draw, field.rendertarget_pixel, scale, dst);
 		}
 
+		const auto blit_overlay = [dst, &field, scale](const Image* pic, const Vector2i& hotspot) {
+			dst->blitrect_scale(Rectf(field.rendertarget_pixel - hotspot.cast<float>() * scale,
+			                          pic->width() * scale, pic->height() * scale),
+			                    pic, Recti(0, 0, pic->width(), pic->height()), 1.f,
+			                    BlendMode::UseAlpha);
+		};
+
+		const auto it = work_area_overlays.find(field.fcoords);
+		if (it != work_area_overlays.end()) {
+			const Image* pic = it->second;
+			blit_overlay(pic, Vector2i(pic->width() / 2, pic->height() / 2));
+		}
+
 		// TODO(sirver): Do not use the field_overlay_manager, instead draw the
 		// overlays we are interested in here directly.
-		field_overlay_manager().foreach_overlay(
-		   field.fcoords, [dst, &field, scale](const Image* pic, const Vector2i& hotspot) {
-			   dst->blitrect_scale(Rectf(field.rendertarget_pixel - hotspot.cast<float>() * scale,
-			                             pic->width() * scale, pic->height() * scale),
-			                       pic, Recti(0, 0, pic->width(), pic->height()), 1.f,
-			                       BlendMode::UseAlpha);
-			});
+		field_overlay_manager().foreach_overlay(field.fcoords, blit_overlay);
+
+		// Blit the selection marker.
+		if (field.fcoords == get_sel_pos().node) {
+			const Image* pic = get_sel_picture();
+			blit_overlay(pic, Vector2i(pic->width() / 2, pic->height() / 2));
+		}
 	}
 }
 


Follow ups