← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/bug-1796364-blinking-buildings into lp:widelands

 

GunChleoc has proposed merging lp:~widelands-dev/widelands/bug-1796364-blinking-buildings into lp:widelands.

Commit message:
Remove z-layering fix for building texts introduced in r8847 because it is buggy and can make building animations disappear

Requested reviews:
  Widelands Developers (widelands-dev)
Related bugs:
  Bug #1796364 in widelands: "Some animation frames suddenly invisible"
  https://bugs.launchpad.net/widelands/+bug/1796364

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/bug-1796364-blinking-buildings/+merge/359348
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/bug-1796364-blinking-buildings into lp:widelands.
=== modified file 'src/economy/flag.h'
--- src/economy/flag.h	2018-09-25 06:32:35 +0000
+++ src/economy/flag.h	2018-11-24 07:34:44 +0000
@@ -26,6 +26,7 @@
 
 #include "base/macros.h"
 #include "economy/routing_node.h"
+#include "logic/map_objects/draw_text.h"
 #include "logic/map_objects/immovable.h"
 #include "logic/map_objects/walkingdir.h"
 
@@ -168,8 +169,11 @@
 	bool init(EditorGameBase&) override;
 	void cleanup(EditorGameBase&) override;
 
-	void
-	draw(uint32_t gametime, const Vector2f& point_on_dst, float scale, RenderTarget* dst) override;
+	void draw(uint32_t gametime,
+	          TextToDraw draw_text,
+	          const Vector2f& point_on_dst,
+	          float scale,
+	          RenderTarget* dst) override;
 
 	static void
 	flag_job_request_callback(Game&, Request&, DescriptionIndex, Worker*, PlayerImmovable&);

=== modified file 'src/economy/portdock.cc'
--- src/economy/portdock.cc	2018-09-14 08:46:36 +0000
+++ src/economy/portdock.cc	2018-11-24 07:34:44 +0000
@@ -140,7 +140,7 @@
 		expedition_bootstrap_->set_economy(e);
 }
 
-void PortDock::draw(uint32_t, const Vector2f&, float, RenderTarget*) {
+void PortDock::draw(uint32_t, const TextToDraw, const Vector2f&, float, RenderTarget*) {
 	// do nothing
 }
 

=== modified file 'src/economy/portdock.h'
--- src/economy/portdock.h	2018-09-25 06:32:35 +0000
+++ src/economy/portdock.h	2018-11-24 07:34:44 +0000
@@ -96,8 +96,11 @@
 
 	Flag& base_flag() override;
 	PositionList get_positions(const EditorGameBase&) const override;
-	void
-	draw(uint32_t gametime, const Vector2f& point_on_dst, float scale, RenderTarget* dst) override;
+	void draw(uint32_t gametime,
+	          TextToDraw draw_text,
+	          const Vector2f& point_on_dst,
+	          float scale,
+	          RenderTarget* dst) override;
 
 	bool init(EditorGameBase&) override;
 	void cleanup(EditorGameBase&) override;

=== modified file 'src/economy/road.h'
--- src/economy/road.h	2018-09-25 06:32:35 +0000
+++ src/economy/road.h	2018-11-24 07:34:44 +0000
@@ -130,8 +130,11 @@
 	bool init(EditorGameBase&) override;
 	void cleanup(EditorGameBase&) override;
 
-	void
-	draw(uint32_t gametime, const Vector2f& point_on_dst, float scale, RenderTarget* dst) override;
+	void draw(uint32_t gametime,
+	          TextToDraw draw_text,
+	          const Vector2f& point_on_dst,
+	          float scale,
+	          RenderTarget* dst) override;
 
 private:
 	void set_path(EditorGameBase&, const Path&);

=== modified file 'src/editor/editorinteractive.cc'
--- src/editor/editorinteractive.cc	2018-10-21 22:21:34 +0000
+++ src/editor/editorinteractive.cc	2018-11-24 07:34:44 +0000
@@ -300,14 +300,14 @@
 		if (draw_immovables_) {
 			Widelands::BaseImmovable* const imm = field.fcoords.field->get_immovable();
 			if (imm != nullptr && imm->get_positions(ebase).front() == field.fcoords) {
-				imm->draw(gametime, field.rendertarget_pixel, scale, &dst);
+				imm->draw(gametime, TextToDraw::kNone, field.rendertarget_pixel, scale, &dst);
 			}
 		}
 
 		if (draw_bobs_) {
 			for (Widelands::Bob* bob = field.fcoords.field->get_first_bob(); bob;
 			     bob = bob->get_next_bob()) {
-				bob->draw(ebase, field.rendertarget_pixel, scale, &dst);
+				bob->draw(ebase, TextToDraw::kNone, field.rendertarget_pixel, scale, &dst);
 			}
 		}
 
@@ -376,8 +376,6 @@
 			}
 		}
 	}
-	// TODO(GunChleoc): If we ever implement an infrastructure tool, the building texts will need to
-	// be blitted here.
 }
 
 /// Needed to get freehand painting tools (hold down mouse and move to edit).

=== modified file 'src/logic/CMakeLists.txt'
--- src/logic/CMakeLists.txt	2018-08-24 07:12:20 +0000
+++ src/logic/CMakeLists.txt	2018-11-24 07:34:44 +0000
@@ -174,6 +174,7 @@
     map_objects/buildcost.h
     map_objects/checkstep.cc
     map_objects/checkstep.h
+    map_objects/draw_text.h
     map_objects/immovable.cc
     map_objects/immovable.h
     map_objects/immovable_program.h
@@ -267,6 +268,7 @@
     game_io
     graphic
     graphic_color
+    graphic_fonthandler
     graphic_image_io
     graphic_playercolor
     graphic_surface

=== modified file 'src/logic/map_objects/bob.cc'
--- src/logic/map_objects/bob.cc	2018-09-14 08:46:36 +0000
+++ src/logic/map_objects/bob.cc	2018-11-24 07:34:44 +0000
@@ -758,6 +758,7 @@
 /// Note that the current node is actually the node that we are walking to, not
 /// the the one that we start from.
 void Bob::draw(const EditorGameBase& egbase,
+               const TextToDraw&,
                const Vector2f& field_on_dst,
                const float scale,
                RenderTarget* dst) const {

=== modified file 'src/logic/map_objects/bob.h'
--- src/logic/map_objects/bob.h	2018-09-25 06:32:35 +0000
+++ src/logic/map_objects/bob.h	2018-11-24 07:34:44 +0000
@@ -25,6 +25,7 @@
 #include "economy/route.h"
 #include "graphic/animation.h"
 #include "graphic/diranimations.h"
+#include "logic/map_objects/draw_text.h"
 #include "logic/map_objects/map_object.h"
 #include "logic/map_objects/walkingdir.h"
 #include "logic/widelands_geometry.h"
@@ -262,8 +263,11 @@
 	// the field associated with this bob (if it is walking, that is its
 	// starting field) in pixel space of 'dst' (including scale). The 'scale' is
 	// required to draw the bob in the right size.
-	virtual void
-	draw(const EditorGameBase&, const Vector2f& field_on_dst, float scale, RenderTarget* dst) const;
+	virtual void draw(const EditorGameBase&,
+	                  const TextToDraw& draw_text,
+	                  const Vector2f& field_on_dst,
+	                  float scale,
+	                  RenderTarget* dst) const;
 
 	// For debug
 	void log_general_info(const EditorGameBase&) const override;

=== added file 'src/logic/map_objects/draw_text.h'
--- src/logic/map_objects/draw_text.h	1970-01-01 00:00:00 +0000
+++ src/logic/map_objects/draw_text.h	2018-11-24 07:34:44 +0000
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2006-2018 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef WL_LOGIC_MAP_OBJECTS_DRAW_TEXT_H
+#define WL_LOGIC_MAP_OBJECTS_DRAW_TEXT_H
+
+enum class TextToDraw {
+	kNone = 0,
+	kCensus = 1,
+	kStatistics = 2,
+};
+
+inline TextToDraw operator|(TextToDraw a, TextToDraw b) {
+	return static_cast<TextToDraw>(static_cast<int>(a) | static_cast<int>(b));
+}
+inline TextToDraw operator&(TextToDraw a, TextToDraw b) {
+	return static_cast<TextToDraw>(static_cast<int>(a) & static_cast<int>(b));
+}
+inline TextToDraw operator~(TextToDraw a) {
+	return static_cast<TextToDraw>(~static_cast<int>(a));
+}
+
+#endif  // end of include guard: WL_LOGIC_MAP_OBJECTS_DRAW_TEXT_H

=== modified file 'src/logic/map_objects/immovable.cc'
--- src/logic/map_objects/immovable.cc	2018-11-23 21:26:24 +0000
+++ src/logic/map_objects/immovable.cc	2018-11-24 07:34:44 +0000
@@ -454,6 +454,7 @@
 }
 
 void Immovable::draw(uint32_t gametime,
+                     const TextToDraw draw_text,
                      const Vector2f& point_on_dst,
                      float scale,
                      RenderTarget* dst) {
@@ -462,12 +463,16 @@
 	}
 	if (!anim_construction_total_) {
 		dst->blit_animation(point_on_dst, scale, anim_, gametime - animstart_);
+		if (former_building_descr_) {
+			do_draw_info(draw_text, former_building_descr_->descname(), "", point_on_dst, scale, dst);
+		}
 	} else {
-		draw_construction(gametime, point_on_dst, scale, dst);
+		draw_construction(gametime, draw_text, point_on_dst, scale, dst);
 	}
 }
 
 void Immovable::draw_construction(const uint32_t gametime,
+                                  const TextToDraw draw_text,
                                   const Vector2f& point_on_dst,
                                   const float scale,
                                   RenderTarget* dst) {
@@ -506,6 +511,13 @@
 
 	dst->blit_animation(
 	   point_on_dst, scale, anim_, current_frame * frametime, player_color, percent);
+
+	// Additionally, if statistics are enabled, draw a progression string
+	do_draw_info(draw_text, descr().descname(),
+	             (boost::format("<font color=%s>%s</font>") % UI_FONT_CLR_DARK.hex_value() %
+	              (boost::format(_("%i%% built")) % (100 * done / total)).str())
+	                .str(),
+	             point_on_dst, scale, dst);
 }
 
 /**
@@ -517,36 +529,6 @@
 	action_data_.reset(data);
 }
 
-std::string Immovable::info_string(const MapObject::InfoStringType format) {
-	std::string result;
-	switch (format) {
-	case MapObject::InfoStringType::kCensus:
-		if (!anim_construction_total_) {
-			if (former_building_descr_) {
-				result = former_building_descr_->descname();
-			}
-		} else {
-			result = descr().descname();
-		}
-		break;
-	case MapObject::InfoStringType::kStatistics:
-		if (anim_construction_total_) {
-			result = (boost::format("<font color=%s>%s</font>") % UI_FONT_CLR_DARK.hex_value() %
-			          (boost::format(_("%i%% built")) %
-			           (100 * anim_construction_done_ / anim_construction_total_))
-			             .str())
-			            .str();
-		} else {
-			result = "";
-		}
-		break;
-	case MapObject::InfoStringType::kTooltip:
-		result = "";
-		break;
-	}
-	return result;
-}
-
 /*
 ==============================
 

=== modified file 'src/logic/map_objects/immovable.h'
--- src/logic/map_objects/immovable.h	2018-09-25 06:32:35 +0000
+++ src/logic/map_objects/immovable.h	2018-11-24 07:34:44 +0000
@@ -26,6 +26,7 @@
 #include "base/macros.h"
 #include "graphic/animation.h"
 #include "logic/map_objects/buildcost.h"
+#include "logic/map_objects/draw_text.h"
 #include "logic/map_objects/map_object.h"
 #include "logic/widelands_geometry.h"
 #include "notifications/note_ids.h"
@@ -96,13 +97,16 @@
 	virtual PositionList get_positions(const EditorGameBase&) const = 0;
 
 	// Draw this immovable onto 'dst' choosing the frame appropriate for
-	// 'gametime'.
+	// 'gametime'. 'draw_text' decides if census and statistics are written too.
 	// The 'coords_to_draw' are passed one to give objects that occupy multiple
 	// fields a way to only draw themselves once. The 'point_on_dst' determines
 	// the point for the hotspot of the animation and 'scale' determines how big
 	// the immovable will be plotted.
-	virtual void
-	draw(uint32_t gametime, const Vector2f& point_on_dst, float scale, RenderTarget* dst) = 0;
+	virtual void draw(uint32_t gametime,
+	                  TextToDraw draw_text,
+	                  const Vector2f& point_on_dst,
+	                  float scale,
+	                  RenderTarget* dst) = 0;
 
 	static int32_t string_to_size(const std::string& size);
 	static std::string size_to_string(int32_t size);
@@ -223,8 +227,11 @@
 	bool init(EditorGameBase&) override;
 	void cleanup(EditorGameBase&) override;
 	void act(Game&, uint32_t data) override;
-	void
-	draw(uint32_t gametime, const Vector2f& point_on_dst, float scale, RenderTarget* dst) override;
+	void draw(uint32_t gametime,
+	          TextToDraw draw_text,
+	          const Vector2f& point_on_dst,
+	          float scale,
+	          RenderTarget* dst) override;
 
 	void switch_program(Game& game, const std::string& programname);
 	bool construct_ware(Game& game, DescriptionIndex index);
@@ -240,8 +247,6 @@
 		return nullptr;
 	}
 
-	std::string info_string(MapObject::InfoStringType format) override;
-
 protected:
 	// The building type that created this immovable, if any.
 	const BuildingDescr* former_building_descr_;
@@ -309,6 +314,7 @@
 
 	void increment_program_pointer();
 	void draw_construction(uint32_t gametime,
+	                       TextToDraw draw_text,
 	                       const Vector2f& point_on_dst,
 	                       float scale,
 	                       RenderTarget* dst);

=== modified file 'src/logic/map_objects/map_object.cc'
--- src/logic/map_objects/map_object.cc	2018-09-23 11:10:56 +0000
+++ src/logic/map_objects/map_object.cc	2018-11-24 07:34:44 +0000
@@ -29,6 +29,7 @@
 
 #include "base/log.h"
 #include "base/wexception.h"
+#include "graphic/font_handler.h"
 #include "graphic/graphic.h"
 #include "graphic/rendertarget.h"
 #include "graphic/text_layout.h"
@@ -468,6 +469,40 @@
 	egbase.objects().remove(*this);
 }
 
+void MapObject::do_draw_info(const TextToDraw& draw_text,
+                             const std::string& census,
+                             const std::string& statictics,
+                             const Vector2f& field_on_dst,
+                             float scale,
+                             RenderTarget* dst) const {
+	if (draw_text == TextToDraw::kNone) {
+		return;
+	}
+
+	// Rendering text is expensive, so let's just do it for only a few sizes.
+	// The forumla is a bit fancy to avoid too much text overlap.
+	scale = std::round(2.f * (scale > 1.f ? std::sqrt(scale) : std::pow(scale, 2.f))) / 2.f;
+	if (scale < 1.f) {
+		return;
+	}
+	const int font_size = scale * UI_FONT_SIZE_SMALL;
+
+	// We always render this so we can have a stable position for the statistics string.
+	std::shared_ptr<const UI::RenderedText> rendered_census =
+	   UI::g_fh->render(as_condensed(census, UI::Align::kCenter, font_size), 120 * scale);
+	Vector2i position = field_on_dst.cast<int>() - Vector2i(0, 48) * scale;
+	if ((draw_text & TextToDraw::kCensus) != TextToDraw::kNone) {
+		rendered_census->draw(*dst, position, UI::Align::kCenter);
+	}
+
+	if ((draw_text & TextToDraw::kStatistics) != TextToDraw::kNone && !statictics.empty()) {
+		std::shared_ptr<const UI::RenderedText> rendered_statistics =
+		   UI::g_fh->render(as_condensed(statictics, UI::Align::kCenter, font_size));
+		position.y += rendered_census->height() + text_height(font_size) / 4;
+		rendered_statistics->draw(*dst, position, UI::Align::kCenter);
+	}
+}
+
 const Image* MapObject::representative_image() const {
 	return descr().representative_image(get_owner() ? &get_owner()->get_playercolor() : nullptr);
 }
@@ -539,10 +574,6 @@
 	reserved_by_worker_ = reserve;
 }
 
-std::string MapObject::info_string(const InfoStringType) {
-	return "";
-}
-
 constexpr uint8_t kCurrentPacketVersionMapObject = 2;
 
 /**

=== modified file 'src/logic/map_objects/map_object.h'
--- src/logic/map_objects/map_object.h	2018-09-24 07:44:52 +0000
+++ src/logic/map_objects/map_object.h	2018-11-24 07:34:44 +0000
@@ -35,6 +35,7 @@
 #include "graphic/color.h"
 #include "graphic/image.h"
 #include "logic/cmd_queue.h"
+#include "logic/map_objects/draw_text.h"
 #include "logic/map_objects/tribes/training_attribute.h"
 #include "logic/widelands.h"
 #include "scripting/lua_table.h"
@@ -339,12 +340,6 @@
 	 */
 	void set_reserved_by_worker(bool reserve);
 
-	enum class InfoStringType { kCensus, kStatistics, kTooltip };
-	/**
-	 * Returns a census, statistics or tooltip string to be shown for this object on the map
-	 */
-	virtual std::string info_string(InfoStringType);
-
 	/**
 	 * Static load functions of derived classes will return a pointer to
 	 * a Loader class. The caller needs to call the virtual functions
@@ -411,6 +406,14 @@
 
 	virtual void cleanup(EditorGameBase&);
 
+	/// Draws census and statistics on screen
+	void do_draw_info(const TextToDraw& draw_text,
+	                  const std::string& census,
+	                  const std::string& statictics,
+	                  const Vector2f& field_on_dst,
+	                  const float scale,
+	                  RenderTarget* dst) const;
+
 #ifdef _WIN32
 	void molog(char const* fmt, ...) const __attribute__((format(gnu_printf, 2, 3)));
 #else

=== modified file 'src/logic/map_objects/tribes/building.cc'
--- src/logic/map_objects/tribes/building.cc	2018-11-23 19:48:39 +0000
+++ src/logic/map_objects/tribes/building.cc	2018-11-24 07:34:44 +0000
@@ -463,20 +463,20 @@
 	}
 }
 
-std::string Building::info_string(const MapObject::InfoStringType format) {
+std::string Building::info_string(const InfoStringFormat& format) {
 	std::string result;
 	switch (format) {
-	case MapObject::InfoStringType::kCensus:
+	case InfoStringFormat::kCensus:
 		if (upcast(ConstructionSite const, constructionsite, this)) {
 			result = constructionsite->building().descname();
 		} else {
 			result = descr().descname();
 		}
 		break;
-	case MapObject::InfoStringType::kStatistics:
+	case InfoStringFormat::kStatistics:
 		result = update_and_get_statistics_string();
 		break;
-	case MapObject::InfoStringType::kTooltip:
+	case InfoStringFormat::kTooltip:
 		if (upcast(ProductionSite const, productionsite, this)) {
 			result = productionsite->production_result();
 		}
@@ -606,6 +606,7 @@
 }
 
 void Building::draw(uint32_t gametime,
+                    const TextToDraw draw_text,
                     const Vector2f& point_on_dst,
                     const float scale,
                     RenderTarget* dst) {
@@ -613,6 +614,24 @@
 	   point_on_dst, scale, anim_, gametime - animstart_, get_owner()->get_playercolor());
 
 	//  door animation?
+
+	//  overlay strings (draw when enabled)
+	draw_info(draw_text, point_on_dst, scale, dst);
+}
+
+/*
+===============
+Draw overlay help strings when enabled.
+===============
+*/
+void Building::draw_info(const TextToDraw draw_text,
+                         const Vector2f& point_on_dst,
+                         const float scale,
+                         RenderTarget* dst) {
+	const std::string statistics_string =
+	   ((draw_text & TextToDraw::kStatistics) != TextToDraw::kNone) ? info_string(InfoStringFormat::kStatistics) : "";
+	do_draw_info(draw_text, info_string(InfoStringFormat::kCensus), statistics_string, point_on_dst,
+	             scale, dst);
 }
 
 int32_t

=== modified file 'src/logic/map_objects/tribes/building.h'
--- src/logic/map_objects/tribes/building.h	2018-11-23 19:48:39 +0000
+++ src/logic/map_objects/tribes/building.h	2018-11-24 07:34:44 +0000
@@ -227,6 +227,8 @@
 	using FormerBuildings = std::vector<DescriptionIndex>;
 
 public:
+	enum class InfoStringFormat { kCensus, kStatistics, kTooltip };
+
 	explicit Building(const BuildingDescr&);
 
 	void load_finish(EditorGameBase&) override;
@@ -242,7 +244,7 @@
 	}
 	PositionList get_positions(const EditorGameBase&) const override;
 
-	std::string info_string(MapObject::InfoStringType format) override;
+	std::string info_string(const InfoStringFormat& format);
 
 	// Return the overlay string that is displayed on the map view when enabled
 	// by the player.
@@ -337,8 +339,13 @@
 	void cleanup(EditorGameBase&) override;
 	void act(Game&, uint32_t data) override;
 
+	void draw(uint32_t gametime,
+	          TextToDraw draw_text,
+	          const Vector2f& point_on_dst,
+	          float scale,
+	          RenderTarget* dst) override;
 	void
-	draw(uint32_t gametime, const Vector2f& point_on_dst, float scale, RenderTarget* dst) override;
+	draw_info(TextToDraw draw_text, const Vector2f& point_on_dst, float scale, RenderTarget* dst);
 
 	void set_seeing(bool see);
 	void set_attack_target(AttackTarget* new_attack_target);

=== modified file 'src/logic/map_objects/tribes/constructionsite.cc'
--- src/logic/map_objects/tribes/constructionsite.cc	2018-08-24 07:12:20 +0000
+++ src/logic/map_objects/tribes/constructionsite.cc	2018-11-24 07:34:44 +0000
@@ -337,6 +337,7 @@
 ===============
 */
 void ConstructionSite::draw(uint32_t gametime,
+                            TextToDraw draw_text,
                             const Vector2f& point_on_dst,
                             float scale,
                             RenderTarget* dst) {
@@ -358,5 +359,8 @@
 	}
 
 	info_.draw(point_on_dst, scale, player_color, dst);
+
+	// Draw help strings
+	draw_info(draw_text, point_on_dst, scale, dst);
 }
 }

=== modified file 'src/logic/map_objects/tribes/constructionsite.h'
--- src/logic/map_objects/tribes/constructionsite.h	2018-09-25 06:32:35 +0000
+++ src/logic/map_objects/tribes/constructionsite.h	2018-11-24 07:34:44 +0000
@@ -120,8 +120,11 @@
 
 	static void wares_queue_callback(Game&, InputQueue*, DescriptionIndex, Worker*, void* data);
 
-	void
-	draw(uint32_t gametime, const Vector2f& point_on_dst, float scale, RenderTarget* dst) override;
+	void draw(uint32_t gametime,
+	          TextToDraw draw_text,
+	          const Vector2f& point_on_dst,
+	          float scale,
+	          RenderTarget* dst) override;
 
 private:
 	int32_t fetchfromflag_;  // # of wares to fetch from flag

=== modified file 'src/logic/map_objects/tribes/dismantlesite.cc'
--- src/logic/map_objects/tribes/dismantlesite.cc	2018-08-24 07:12:20 +0000
+++ src/logic/map_objects/tribes/dismantlesite.cc	2018-11-24 07:34:44 +0000
@@ -217,6 +217,7 @@
 ===============
 */
 void DismantleSite::draw(uint32_t gametime,
+                         const TextToDraw draw_text,
                          const Vector2f& point_on_dst,
                          float scale,
                          RenderTarget* dst) {
@@ -229,5 +230,8 @@
 	// Blit bottom part of the animation according to dismantle progress
 	dst->blit_animation(point_on_dst, scale, building_->get_unoccupied_animation(), tanim,
 	                    player_color, 100 - ((get_built_per64k() * 100) >> 16));
+
+	// Draw help strings
+	draw_info(draw_text, point_on_dst, scale, dst);
 }
 }

=== modified file 'src/logic/map_objects/tribes/dismantlesite.h'
--- src/logic/map_objects/tribes/dismantlesite.h	2018-09-25 06:32:35 +0000
+++ src/logic/map_objects/tribes/dismantlesite.h	2018-11-24 07:34:44 +0000
@@ -87,8 +87,11 @@
 		return DISMANTLESITE_STEP_TIME;
 	}
 
-	void
-	draw(uint32_t gametime, const Vector2f& point_on_dst, float scale, RenderTarget* dst) override;
+	void draw(uint32_t gametime,
+	          TextToDraw draw_text,
+	          const Vector2f& point_on_dst,
+	          float scale,
+	          RenderTarget* dst) override;
 };
 }
 

=== modified file 'src/logic/map_objects/tribes/ship.cc'
--- src/logic/map_objects/tribes/ship.cc	2018-09-29 14:35:29 +0000
+++ src/logic/map_objects/tribes/ship.cc	2018-11-24 07:34:44 +0000
@@ -977,53 +977,55 @@
 	ship_wakeup(game);
 }
 
-std::string Ship::info_string(const MapObject::InfoStringType format) {
+void Ship::draw(const EditorGameBase& egbase,
+                const TextToDraw& draw_text,
+                const Vector2f& field_on_dst,
+                const float scale,
+                RenderTarget* dst) const {
+	Bob::draw(egbase, draw_text, field_on_dst, scale, dst);
+
 	// Show ship name and current activity
-	std::string result;
-	switch (format) {
-	case MapObject::InfoStringType::kCensus:
-		result = shipname_;
-		break;
-	case MapObject::InfoStringType::kStatistics: {
+	std::string statistics_string;
+	if ((draw_text & TextToDraw::kStatistics) != TextToDraw::kNone) {
 		switch (ship_state_) {
 		case (ShipStates::kTransport):
 			if (destination_.is_set()) {
 				/** TRANSLATORS: This is a ship state. The ship is currently transporting wares. */
-				result = pgettext("ship_state", "Shipping");
+				statistics_string = pgettext("ship_state", "Shipping");
 			} else {
 				/** TRANSLATORS: This is a ship state. The ship is ready to transport wares, but has
 				 * nothing to do. */
-				result = pgettext("ship_state", "Idle");
+				statistics_string = pgettext("ship_state", "Idle");
 			}
 			break;
 		case (ShipStates::kExpeditionWaiting):
 			/** TRANSLATORS: This is a ship state. An expedition is waiting for your commands. */
-			result = pgettext("ship_state", "Waiting");
+			statistics_string = pgettext("ship_state", "Waiting");
 			break;
 		case (ShipStates::kExpeditionScouting):
 			/** TRANSLATORS: This is a ship state. An expedition is scouting for port spaces. */
-			result = pgettext("ship_state", "Scouting");
+			statistics_string = pgettext("ship_state", "Scouting");
 			break;
 		case (ShipStates::kExpeditionPortspaceFound):
 			/** TRANSLATORS: This is a ship state. An expedition has found a port space. */
-			result = pgettext("ship_state", "Port Space Found");
+			statistics_string = pgettext("ship_state", "Port Space Found");
 			break;
 		case (ShipStates::kExpeditionColonizing):
 			/** TRANSLATORS: This is a ship state. An expedition is unloading wares/workers to build a
 			 * port. */
-			result = pgettext("ship_state", "Founding a Colony");
+			statistics_string = pgettext("ship_state", "Founding a Colony");
 			break;
 		case (ShipStates::kSinkRequest):
 		case (ShipStates::kSinkAnimation):
 			break;
 		}
-		result =
-		   (boost::format("<font color=%s>%s</font>") % UI_FONT_CLR_OK.hex_value() % result).str();
-	} break;
-	case MapObject::InfoStringType::kTooltip:
-		result = "";
+		statistics_string = (boost::format("<font color=%s>%s</font>") % UI_FONT_CLR_OK.hex_value() %
+		                     statistics_string)
+		                       .str();
 	}
-	return result;
+
+	do_draw_info(draw_text, shipname_, statistics_string, calc_drawpos(egbase, field_on_dst, scale),
+	             scale, dst);
 }
 
 void Ship::log_general_info(const EditorGameBase& egbase) const {

=== modified file 'src/logic/map_objects/tribes/ship.h'
--- src/logic/map_objects/tribes/ship.h	2018-09-24 07:44:52 +0000
+++ src/logic/map_objects/tribes/ship.h	2018-11-24 07:34:44 +0000
@@ -230,7 +230,13 @@
 
 	void exp_cancel(Game&);
 	void sink_ship(Game&);
-	std::string info_string(MapObject::InfoStringType format) override;
+
+protected:
+	void draw(const EditorGameBase&,
+	          const TextToDraw& draw_text,
+	          const Vector2f& field_on_dst,
+	          float scale,
+	          RenderTarget* dst) const override;
 
 private:
 	friend struct Fleet;

=== modified file 'src/logic/map_objects/tribes/soldier.cc'
--- src/logic/map_objects/tribes/soldier.cc	2018-09-14 08:46:36 +0000
+++ src/logic/map_objects/tribes/soldier.cc	2018-11-24 07:34:44 +0000
@@ -439,6 +439,7 @@
  * Draw this soldier. This basically draws him as a worker, but add health points
  */
 void Soldier::draw(const EditorGameBase& game,
+                   const TextToDraw&,
                    const Vector2f& field_on_dst,
                    const float scale,
                    RenderTarget* dst) const {

=== modified file 'src/logic/map_objects/tribes/soldier.h'
--- src/logic/map_objects/tribes/soldier.h	2018-09-14 08:46:36 +0000
+++ src/logic/map_objects/tribes/soldier.h	2018-11-24 07:34:44 +0000
@@ -209,6 +209,7 @@
 
 	/// Draw this soldier
 	void draw(const EditorGameBase&,
+	          const TextToDraw& draw_text,
 	          const Vector2f& point_on_dst,
 	          float scale,
 	          RenderTarget* dst) const override;

=== modified file 'src/logic/map_objects/tribes/worker.cc'
--- src/logic/map_objects/tribes/worker.cc	2018-11-23 21:26:24 +0000
+++ src/logic/map_objects/tribes/worker.cc	2018-11-24 07:34:44 +0000
@@ -2978,6 +2978,7 @@
  * Draw the worker, taking the carried ware into account.
  */
 void Worker::draw(const EditorGameBase& egbase,
+                  const TextToDraw&,
                   const Vector2f& field_on_dst,
                   const float scale,
                   RenderTarget* dst) const {

=== modified file 'src/logic/map_objects/tribes/worker.h'
--- src/logic/map_objects/tribes/worker.h	2018-09-14 08:46:36 +0000
+++ src/logic/map_objects/tribes/worker.h	2018-11-24 07:34:44 +0000
@@ -182,6 +182,7 @@
 	                        const float scale,
 	                        RenderTarget* dst) const;
 	void draw(const EditorGameBase&,
+	          const TextToDraw& draw_text,
 	          const Vector2f& field_on_dst,
 	          float scale,
 	          RenderTarget* dst) const override;

=== modified file 'src/wui/interactive_base.cc'
--- src/wui/interactive_base.cc	2018-08-24 07:12:20 +0000
+++ src/wui/interactive_base.cc	2018-11-24 07:34:44 +0000
@@ -223,7 +223,7 @@
 				     player.is_hostile(*productionsite->get_owner())))
 					return set_tooltip("");
 			}
-			set_tooltip(productionsite->info_string(Widelands::MapObject::InfoStringType::kTooltip));
+			set_tooltip(productionsite->info_string(Widelands::Building::InfoStringFormat::kTooltip));
 			return;
 		}
 	set_tooltip("");

=== modified file 'src/wui/interactive_base.h'
--- src/wui/interactive_base.h	2018-08-24 07:12:20 +0000
+++ src/wui/interactive_base.h	2018-11-24 07:34:44 +0000
@@ -47,16 +47,6 @@
 class EdgeOverlayManager;
 class UniqueWindowHandler;
 
-enum TextToDraw {
-	kNone = 0,
-	kCensus = 1,
-	kStatistics = 2,
-};
-
-inline TextToDraw operator|(TextToDraw a, TextToDraw b) {
-	return static_cast<TextToDraw>(static_cast<int>(a) | static_cast<int>(b));
-}
-
 /**
  * This is used to represent the code that InteractivePlayer and
  * EditorInteractive share.

=== modified file 'src/wui/interactive_gamebase.cc'
--- src/wui/interactive_gamebase.cc	2018-09-25 06:32:35 +0000
+++ src/wui/interactive_gamebase.cc	2018-11-24 07:34:44 +0000
@@ -56,46 +56,6 @@
 	return _("PAUSE");
 }
 
-// Draws census and statistics on screen for the given map object
-void draw_mapobject_infotext(RenderTarget* dst,
-                             const Vector2i& init_position,
-                             float scale,
-                             Widelands::MapObject* mapobject,
-                             const TextToDraw text_to_draw) {
-	const std::string census_string =
-	   mapobject->info_string(Widelands::MapObject::InfoStringType::kCensus);
-	if (census_string.empty()) {
-		// If there is no census available for the map object, we also won't have any statistics.
-		return;
-	}
-
-	const std::string statistics_string =
-	   (text_to_draw & TextToDraw::kStatistics) ?
-	      mapobject->info_string(Widelands::MapObject::InfoStringType::kStatistics) :
-	      "";
-	if (census_string.empty() && statistics_string.empty()) {
-		// Nothing to do
-		return;
-	}
-
-	const int font_size = scale * UI_FONT_SIZE_SMALL;
-
-	// We always render this so we can have a stable position for the statistics string.
-	std::shared_ptr<const UI::RenderedText> rendered_census =
-	   UI::g_fh->render(as_condensed(census_string, UI::Align::kCenter, font_size), 120 * scale);
-	Vector2i position = init_position - Vector2i(0, 48) * scale;
-	if (text_to_draw & TextToDraw::kCensus) {
-		rendered_census->draw(*dst, position, UI::Align::kCenter);
-	}
-
-	if (!statistics_string.empty()) {
-		std::shared_ptr<const UI::RenderedText> rendered_statistics =
-		   UI::g_fh->render(as_condensed(statistics_string, UI::Align::kCenter, font_size));
-		position.y += rendered_census->height() + text_height(font_size) / 4;
-		rendered_statistics->draw(*dst, position, UI::Align::kCenter);
-	}
-}
-
 }  // namespace
 
 InteractiveGameBase::InteractiveGameBase(Widelands::Game& g,
@@ -179,34 +139,6 @@
 	}
 }
 
-void InteractiveGameBase::draw_mapobject_infotexts(
-   RenderTarget* dst,
-   float scale,
-   const std::vector<std::pair<Vector2i, Widelands::MapObject*>>& mapobjects_to_draw_text_for,
-   const TextToDraw text_to_draw,
-   const Widelands::Player* plr) const {
-	// Rendering text is expensive, so let's just do it for only a few sizes.
-	// The formula is a bit fancy to avoid too much text overlap.
-	const float scale_for_text =
-	   std::round(2.f * (scale > 1.f ? std::sqrt(scale) : std::pow(scale, 2.f))) / 2.f;
-	if (scale_for_text < 1.f) {
-		return;
-	}
-
-	for (const auto& draw_my_text : mapobjects_to_draw_text_for) {
-		TextToDraw draw_text_for_this_mapobject = text_to_draw;
-		const Widelands::Player* owner = draw_my_text.second->get_owner();
-		if (owner != nullptr && plr != nullptr && !plr->see_all() && plr->is_hostile(*owner)) {
-			draw_text_for_this_mapobject =
-			   static_cast<TextToDraw>(draw_text_for_this_mapobject & ~TextToDraw::kStatistics);
-		}
-		if (draw_text_for_this_mapobject != TextToDraw::kNone) {
-			draw_mapobject_infotext(dst, draw_my_text.first, scale_for_text, draw_my_text.second,
-			                        draw_text_for_this_mapobject);
-		}
-	}
-}
-
 /**
  * Called for every game after loading (from a savegame or just from a map
  * during single/multiplayer/scenario).

=== modified file 'src/wui/interactive_gamebase.h'
--- src/wui/interactive_gamebase.h	2018-09-25 06:32:35 +0000
+++ src/wui/interactive_gamebase.h	2018-11-24 07:34:44 +0000
@@ -102,13 +102,6 @@
 	void start() override;
 
 protected:
-	/// Draws census and statistics on screen for the listed mapobjects
-	void draw_mapobject_infotexts(
-	   RenderTarget* dst,
-	   float scale,
-	   const std::vector<std::pair<Vector2i, Widelands::MapObject*>>& mapobjects_to_draw_text_for,
-	   const TextToDraw text_to_draw,
-	   const Widelands::Player* plr) const;
 	void draw_overlay(RenderTarget&) override;
 
 	GameMainMenuWindows main_windows_;

=== modified file 'src/wui/interactive_player.cc'
--- src/wui/interactive_player.cc	2018-09-29 14:35:29 +0000
+++ src/wui/interactive_player.cc	2018-11-24 07:34:44 +0000
@@ -85,21 +85,42 @@
 	return brightness / 255.;
 }
 
-// Draws immovable if the field matches its actual position and returns true if the immovable was
-// drawn there.
-bool draw_immovable_for_visible_field(const Widelands::EditorGameBase& egbase,
-                                      const FieldsToDraw::Field& field,
-                                      const float scale,
-                                      Widelands::BaseImmovable* const imm,
-                                      RenderTarget* dst) {
+// Remove statistics from the text to draw if the player does not match the map object's owner
+TextToDraw filter_text_to_draw(const TextToDraw text_to_draw, const Widelands::MapObject* object, const Widelands::Player& player) {
+	TextToDraw result = text_to_draw;
+	const Widelands::Player* owner = object->get_owner();
+	if (owner != nullptr && !player.see_all() && player.is_hostile(*owner)) {
+		result = result & ~TextToDraw::kStatistics;
+	}
+	return result;
+}
+
+void draw_immovable_for_visible_field(const Widelands::EditorGameBase& egbase,
+                                       const FieldsToDraw::Field& field,
+                                       const float scale,
+                                       const TextToDraw text_to_draw,
+                                       const Widelands::Player& player,
+                                       RenderTarget* dst) {
+	Widelands::BaseImmovable* const imm = field.fcoords.field->get_immovable();
 	if (imm != nullptr && imm->get_positions(egbase).front() == field.fcoords) {
-		imm->draw(egbase.get_gametime(), field.rendertarget_pixel, scale, dst);
-		return true;
-	}
-	return false;
-}
-
-void draw_immovables_for_formerly_visible_field(const FieldsToDraw::Field& field,
+		imm->draw(
+		   egbase.get_gametime(), filter_text_to_draw(text_to_draw, imm, player), field.rendertarget_pixel, scale, dst);
+	}
+}
+
+void draw_bobs_for_visible_field(const Widelands::EditorGameBase& egbase,
+                                 const FieldsToDraw::Field& field,
+                                 const float scale,
+                                 const TextToDraw text_to_draw,
+                                 const Widelands::Player& player,
+                                 RenderTarget* dst) {
+	for (Widelands::Bob* bob = field.fcoords.field->get_first_bob(); bob;
+	     bob = bob->get_next_bob()) {
+		bob->draw(egbase, filter_text_to_draw(text_to_draw, bob, player), field.rendertarget_pixel, scale, dst);
+	}
+}
+
+void draw_immovable_for_formerly_visible_field(const FieldsToDraw::Field& field,
                                                 const Widelands::Player::Field& player_field,
                                                 const float scale,
                                                 RenderTarget* dst) {
@@ -267,8 +288,7 @@
 	auto* fields_to_draw = given_map_view->draw_terrain(gbase, dst);
 	const auto& road_building = road_building_overlays();
 	const std::map<Widelands::Coords, const Image*> workarea_overlays = get_workarea_overlays(map);
-	std::vector<std::pair<Vector2i, Widelands::MapObject*>> mapobjects_to_draw_text_for;
-	const auto text_to_draw = get_text_to_draw();
+
 	const float scale = 1.f / given_map_view->view().zoom;
 
 	for (size_t idx = 0; idx < fields_to_draw->size(); ++idx) {
@@ -299,21 +319,12 @@
 
 			// Render stuff that belongs to the node.
 			if (f->vision > 1) {
-				Widelands::BaseImmovable* imm = f->fcoords.field->get_immovable();
-				if (draw_immovable_for_visible_field(gbase, *f, scale, imm, dst)) {
-					mapobjects_to_draw_text_for.push_back(
-					   std::make_pair(f->rendertarget_pixel.cast<int>(), imm));
-				}
-
-				for (Widelands::Bob* bob = f->fcoords.field->get_first_bob(); bob;
-				     bob = bob->get_next_bob()) {
-					bob->draw(gbase, f->rendertarget_pixel, scale, dst);
-					mapobjects_to_draw_text_for.push_back(std::make_pair(
-					   bob->calc_drawpos(gbase, f->rendertarget_pixel, scale).cast<int>(), bob));
-				}
+				const auto text_to_draw = get_text_to_draw();
+				draw_immovable_for_visible_field(gbase, *f, scale, text_to_draw, plr, dst);
+				draw_bobs_for_visible_field(gbase, *f, scale, text_to_draw, plr, dst);
 			} else if (f->vision == 1) {
 				// We never show census or statistics for objects in the fog.
-				draw_immovables_for_formerly_visible_field(*f, player_field, scale, dst);
+				draw_immovable_for_formerly_visible_field(*f, player_field, scale, dst);
 			}
 		}
 
@@ -352,8 +363,6 @@
 			}
 		}
 	}
-	// Blit census & Statistics.
-	draw_mapobject_infotexts(dst, scale, mapobjects_to_draw_text_for, text_to_draw, &plr);
 }
 
 void InteractivePlayer::popup_message(Widelands::MessageId const id,

=== modified file 'src/wui/interactive_spectator.cc'
--- src/wui/interactive_spectator.cc	2018-09-29 14:35:29 +0000
+++ src/wui/interactive_spectator.cc	2018-11-24 07:34:44 +0000
@@ -122,8 +122,6 @@
 
 	const auto text_to_draw = get_text_to_draw();
 	const std::map<Widelands::Coords, const Image*> workarea_overlays = get_workarea_overlays(map);
-	std::vector<std::pair<Vector2i, Widelands::MapObject*>> mapobjects_to_draw_text_for;
-
 	for (size_t idx = 0; idx < fields_to_draw->size(); ++idx) {
 		const FieldsToDraw::Field& field = fields_to_draw->at(idx);
 
@@ -131,16 +129,12 @@
 
 		Widelands::BaseImmovable* const imm = field.fcoords.field->get_immovable();
 		if (imm != nullptr && imm->get_positions(the_game).front() == field.fcoords) {
-			imm->draw(gametime, field.rendertarget_pixel, scale, dst);
-			mapobjects_to_draw_text_for.push_back(
-			   std::make_pair(field.rendertarget_pixel.cast<int>(), imm));
+			imm->draw(gametime, text_to_draw, field.rendertarget_pixel, scale, dst);
 		}
 
 		for (Widelands::Bob* bob = field.fcoords.field->get_first_bob(); bob;
 		     bob = bob->get_next_bob()) {
-			bob->draw(the_game, field.rendertarget_pixel, scale, dst);
-			mapobjects_to_draw_text_for.push_back(std::make_pair(
-			   bob->calc_drawpos(the_game, field.rendertarget_pixel, scale).cast<int>(), bob));
+			bob->draw(the_game, text_to_draw, field.rendertarget_pixel, scale, dst);
 		}
 
 		// Draw work area previews.
@@ -173,9 +167,6 @@
 			blit_field_overlay(dst, field, pic, Vector2i(pic->width() / 2, pic->height() / 2), scale);
 		}
 	}
-
-	// Blit census & Statistics.
-	draw_mapobject_infotexts(dst, scale, mapobjects_to_draw_text_for, text_to_draw, nullptr);
 }
 
 /**

=== modified file 'src/wui/transport_draw.cc'
--- src/wui/transport_draw.cc	2018-09-25 06:32:35 +0000
+++ src/wui/transport_draw.cc	2018-11-24 07:34:44 +0000
@@ -26,7 +26,11 @@
 
 namespace Widelands {
 
-void Flag::draw(uint32_t gametime, const Vector2f& point_on_dst, float scale, RenderTarget* dst) {
+void Flag::draw(uint32_t gametime,
+                const TextToDraw,
+                const Vector2f& point_on_dst,
+                float scale,
+                RenderTarget* dst) {
 	static struct {
 		float x, y;
 	} ware_offsets[8] = {{-5.f, 1.f},  {-1.f, 3.f},  {3.f, 3.f},  {7.f, 1.f},
@@ -50,6 +54,6 @@
 }
 
 /** The road is drawn by the terrain renderer via marked fields. */
-void Road::draw(uint32_t, const Vector2f&, float, RenderTarget*) {
+void Road::draw(uint32_t, const TextToDraw, const Vector2f&, float, RenderTarget*) {
 }
 }


Follow ups