← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/bug-1480927-building-texts into lp:widelands

 

GunChleoc has proposed merging lp:~widelands-dev/widelands/bug-1480927-building-texts into lp:widelands.

Commit message:
Census and statistics for mapobjects are now drawn in InteractiveGamebase. This prevents text from disappearing behind graphics.

Requested reviews:
  Widelands Developers (widelands-dev)
Related bugs:
  Bug #1480927 in widelands: "Building description hidden by other buildings"
  https://bugs.launchpad.net/widelands/+bug/1480927

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/bug-1480927-building-texts/+merge/353728
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/bug-1480927-building-texts into lp:widelands.
=== modified file 'src/economy/flag.h'
--- src/economy/flag.h	2018-08-09 11:11:15 +0000
+++ src/economy/flag.h	2018-08-24 18:28:37 +0000
@@ -26,7 +26,6 @@
 
 #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"
 
@@ -170,7 +169,6 @@
 	void cleanup(EditorGameBase&) override;
 
 	void draw(uint32_t gametime,
-	          TextToDraw draw_text,
 	          const Vector2f& point_on_dst,
 	          float scale,
 	          RenderTarget* dst) override;

=== modified file 'src/economy/portdock.cc'
--- src/economy/portdock.cc	2018-04-16 07:03:12 +0000
+++ src/economy/portdock.cc	2018-08-24 18:28:37 +0000
@@ -140,7 +140,7 @@
 		expedition_bootstrap_->set_economy(e);
 }
 
-void PortDock::draw(uint32_t, const TextToDraw, const Vector2f&, float, RenderTarget*) {
+void PortDock::draw(uint32_t, const Vector2f&, float, RenderTarget*) {
 	// do nothing
 }
 

=== modified file 'src/economy/portdock.h'
--- src/economy/portdock.h	2018-04-07 16:59:00 +0000
+++ src/economy/portdock.h	2018-08-24 18:28:37 +0000
@@ -97,7 +97,6 @@
 	Flag& base_flag() override;
 	PositionList get_positions(const EditorGameBase&) const override;
 	void draw(uint32_t gametime,
-	          TextToDraw draw_text,
 	          const Vector2f& point_on_dst,
 	          float scale,
 	          RenderTarget* dst) override;

=== modified file 'src/economy/road.h'
--- src/economy/road.h	2018-07-12 06:02:15 +0000
+++ src/economy/road.h	2018-08-24 18:28:37 +0000
@@ -131,7 +131,6 @@
 	void cleanup(EditorGameBase&) override;
 
 	void draw(uint32_t gametime,
-	          TextToDraw draw_text,
 	          const Vector2f& point_on_dst,
 	          float scale,
 	          RenderTarget* dst) override;

=== modified file 'src/editor/editorinteractive.cc'
--- src/editor/editorinteractive.cc	2018-07-20 08:42:23 +0000
+++ src/editor/editorinteractive.cc	2018-08-24 18:28:37 +0000
@@ -296,14 +296,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, TextToDraw::kNone, field.rendertarget_pixel, scale, &dst);
+				imm->draw(gametime, 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, TextToDraw::kNone, field.rendertarget_pixel, scale, &dst);
+				bob->draw(ebase, field.rendertarget_pixel, scale, &dst);
 			}
 		}
 
@@ -372,6 +372,7 @@
 			}
 		}
 	}
+	// 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/graphic/game_renderer.h'
--- src/graphic/game_renderer.h	2018-04-07 16:59:00 +0000
+++ src/graphic/game_renderer.h	2018-08-24 18:28:37 +0000
@@ -27,7 +27,6 @@
 #include "base/vector.h"
 #include "graphic/gl/fields_to_draw.h"
 #include "logic/editor_game_base.h"
-#include "logic/map_objects/draw_text.h"
 #include "logic/player.h"
 
 // Draw the terrain only.

=== modified file 'src/logic/CMakeLists.txt'
--- src/logic/CMakeLists.txt	2018-05-13 07:15:39 +0000
+++ src/logic/CMakeLists.txt	2018-08-24 18:28:37 +0000
@@ -174,7 +174,6 @@
     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
@@ -268,7 +267,6 @@
     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-04-27 06:11:05 +0000
+++ src/logic/map_objects/bob.cc	2018-08-24 18:28:37 +0000
@@ -758,7 +758,6 @@
 /// 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-04-07 16:59:00 +0000
+++ src/logic/map_objects/bob.h	2018-08-24 18:28:37 +0000
@@ -25,7 +25,6 @@
 #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"
@@ -264,7 +263,6 @@
 	// 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 TextToDraw& draw_text,
 	                  const Vector2f& field_on_dst,
 	                  float scale,
 	                  RenderTarget* dst) const;

=== removed file 'src/logic/map_objects/draw_text.h'
--- src/logic/map_objects/draw_text.h	2018-04-07 16:59:00 +0000
+++ src/logic/map_objects/draw_text.h	1970-01-01 00:00:00 +0000
@@ -1,33 +0,0 @@
-/*
- * 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 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));
-}
-
-#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-07-13 07:46:36 +0000
+++ src/logic/map_objects/immovable.cc	2018-08-24 18:28:37 +0000
@@ -454,7 +454,6 @@
 }
 
 void Immovable::draw(uint32_t gametime,
-                     const TextToDraw draw_text,
                      const Vector2f& point_on_dst,
                      float scale,
                      RenderTarget* dst) {
@@ -463,16 +462,12 @@
 	}
 	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, draw_text, point_on_dst, scale, dst);
+		draw_construction(gametime, 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) {
@@ -511,13 +506,6 @@
 
 	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);
 }
 
 /**
@@ -529,6 +517,34 @@
 	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-07-08 16:10:50 +0000
+++ src/logic/map_objects/immovable.h	2018-08-24 18:28:37 +0000
@@ -26,7 +26,6 @@
 #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"
@@ -97,13 +96,12 @@
 	virtual PositionList get_positions(const EditorGameBase&) const = 0;
 
 	// Draw this immovable onto 'dst' choosing the frame appropriate for
-	// 'gametime'. 'draw_text' decides if census and statistics are written too.
+	// 'gametime'.
 	// 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,
-	                  TextToDraw draw_text,
 	                  const Vector2f& point_on_dst,
 	                  float scale,
 	                  RenderTarget* dst) = 0;
@@ -228,7 +226,6 @@
 	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;
@@ -247,6 +244,8 @@
 		return nullptr;
 	}
 
+	std::string info_string(const MapObject::InfoStringType& format) override;
+
 protected:
 	// The building type that created this immovable, if any.
 	const BuildingDescr* former_building_descr_;
@@ -314,7 +313,6 @@
 
 	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-07-23 09:04:47 +0000
+++ src/logic/map_objects/map_object.cc	2018-08-24 18:28:37 +0000
@@ -29,7 +29,6 @@
 
 #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"
@@ -469,40 +468,6 @@
 	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) {
-		rendered_census->draw(*dst, position, UI::Align::kCenter);
-	}
-
-	if (draw_text & TextToDraw::kStatistics && !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);
 }
@@ -574,6 +539,10 @@
 	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-05-12 04:18:21 +0000
+++ src/logic/map_objects/map_object.h	2018-08-24 18:28:37 +0000
@@ -35,7 +35,6 @@
 #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"
@@ -330,7 +329,6 @@
 		HeaderFleet = 11,
 	};
 
-public:
 	/**
 	 * Returns whether this immovable was reserved by a worker.
 	 */
@@ -341,6 +339,12 @@
 	 */
 	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(const InfoStringType&);
+
 	/**
 	 * Static load functions of derived classes will return a pointer to
 	 * a Loader class. The caller needs to call the virtual functions
@@ -407,14 +411,6 @@
 
 	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-04-27 06:11:05 +0000
+++ src/logic/map_objects/tribes/building.cc	2018-08-24 18:28:37 +0000
@@ -457,20 +457,20 @@
 	}
 }
 
-std::string Building::info_string(const InfoStringFormat& format) {
+std::string Building::info_string(const MapObject::InfoStringType& format) {
 	std::string result;
 	switch (format) {
-	case InfoStringFormat::kCensus:
+	case MapObject::InfoStringType::kCensus:
 		if (upcast(ConstructionSite const, constructionsite, this)) {
 			result = constructionsite->building().descname();
 		} else {
 			result = descr().descname();
 		}
 		break;
-	case InfoStringFormat::kStatistics:
+	case MapObject::InfoStringType::kStatistics:
 		result = update_and_get_statistics_string();
 		break;
-	case InfoStringFormat::kTooltip:
+	case MapObject::InfoStringType::kTooltip:
 		if (upcast(ProductionSite const, productionsite, this)) {
 			result = productionsite->production_result();
 		}
@@ -600,7 +600,6 @@
 }
 
 void Building::draw(uint32_t gametime,
-                    const TextToDraw draw_text,
                     const Vector2f& point_on_dst,
                     const float scale,
                     RenderTarget* dst) {
@@ -608,24 +607,6 @@
 	   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) ? 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-05-24 10:19:21 +0000
+++ src/logic/map_objects/tribes/building.h	2018-08-24 18:28:37 +0000
@@ -223,8 +223,6 @@
 	using FormerBuildings = std::vector<DescriptionIndex>;
 
 public:
-	enum class InfoStringFormat { kCensus, kStatistics, kTooltip };
-
 	explicit Building(const BuildingDescr&);
 
 	void load_finish(EditorGameBase&) override;
@@ -240,7 +238,7 @@
 	}
 	PositionList get_positions(const EditorGameBase&) const override;
 
-	std::string info_string(const InfoStringFormat& format);
+	std::string info_string(const MapObject::InfoStringType& format) override;
 
 	// Return the overlay string that is displayed on the map view when enabled
 	// by the player.
@@ -336,12 +334,9 @@
 	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_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-05-07 19:11:53 +0000
+++ src/logic/map_objects/tribes/constructionsite.cc	2018-08-24 18:28:37 +0000
@@ -337,7 +337,6 @@
 ===============
 */
 void ConstructionSite::draw(uint32_t gametime,
-                            TextToDraw draw_text,
                             const Vector2f& point_on_dst,
                             float scale,
                             RenderTarget* dst) {
@@ -359,8 +358,5 @@
 	}
 
 	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-04-07 16:59:00 +0000
+++ src/logic/map_objects/tribes/constructionsite.h	2018-08-24 18:28:37 +0000
@@ -121,7 +121,6 @@
 	static void wares_queue_callback(Game&, InputQueue*, DescriptionIndex, Worker*, void* data);
 
 	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/dismantlesite.cc'
--- src/logic/map_objects/tribes/dismantlesite.cc	2018-04-27 06:11:05 +0000
+++ src/logic/map_objects/tribes/dismantlesite.cc	2018-08-24 18:28:37 +0000
@@ -217,7 +217,6 @@
 ===============
 */
 void DismantleSite::draw(uint32_t gametime,
-                         const TextToDraw draw_text,
                          const Vector2f& point_on_dst,
                          float scale,
                          RenderTarget* dst) {
@@ -230,8 +229,5 @@
 	// 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-04-07 16:59:00 +0000
+++ src/logic/map_objects/tribes/dismantlesite.h	2018-08-24 18:28:37 +0000
@@ -88,7 +88,6 @@
 	}
 
 	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-07-08 15:16:16 +0000
+++ src/logic/map_objects/tribes/ship.cc	2018-08-24 18:28:37 +0000
@@ -976,55 +976,54 @@
 	ship_wakeup(game);
 }
 
-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);
-
+std::string Ship::info_string(const MapObject::InfoStringType& format) {
 	// Show ship name and current activity
-	std::string statistics_string;
-	if (draw_text & TextToDraw::kStatistics) {
+	std::string result;
+	switch (format) {
+	case MapObject::InfoStringType::kCensus:
+		result = descr().descname();
+		break;
+	case MapObject::InfoStringType::kStatistics: {
 		switch (ship_state_) {
 		case (ShipStates::kTransport):
 			if (destination_.is_set()) {
 				/** TRANSLATORS: This is a ship state. The ship is currently transporting wares. */
-				statistics_string = pgettext("ship_state", "Shipping");
+				result = pgettext("ship_state", "Shipping");
 			} else {
 				/** TRANSLATORS: This is a ship state. The ship is ready to transport wares, but has
 				 * nothing to do. */
-				statistics_string = pgettext("ship_state", "Idle");
+				result = pgettext("ship_state", "Idle");
 			}
 			break;
 		case (ShipStates::kExpeditionWaiting):
 			/** TRANSLATORS: This is a ship state. An expedition is waiting for your commands. */
-			statistics_string = pgettext("ship_state", "Waiting");
+			result = pgettext("ship_state", "Waiting");
 			break;
 		case (ShipStates::kExpeditionScouting):
 			/** TRANSLATORS: This is a ship state. An expedition is scouting for port spaces. */
-			statistics_string = pgettext("ship_state", "Scouting");
+			result = pgettext("ship_state", "Scouting");
 			break;
 		case (ShipStates::kExpeditionPortspaceFound):
 			/** TRANSLATORS: This is a ship state. An expedition has found a port space. */
-			statistics_string = pgettext("ship_state", "Port Space Found");
+			result = 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. */
-			statistics_string = pgettext("ship_state", "Founding a Colony");
+			result = pgettext("ship_state", "Founding a Colony");
 			break;
 		case (ShipStates::kSinkRequest):
 		case (ShipStates::kSinkAnimation):
 			break;
 		}
-		statistics_string = (boost::format("<font color=%s>%s</font>") % UI_FONT_CLR_OK.hex_value() %
-		                     statistics_string)
+		result = (boost::format("<font color=%s>%s</font>") % UI_FONT_CLR_OK.hex_value() %
+		                     result)
 		                       .str();
+	} break;
+		case MapObject::InfoStringType::kTooltip:
+		result = "";
 	}
-
-	do_draw_info(draw_text, shipname_, statistics_string, calc_drawpos(egbase, field_on_dst, scale),
-	             scale, dst);
+	return result;
 }
 
 void Ship::log_general_info(const EditorGameBase& egbase) {

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

=== modified file 'src/logic/map_objects/tribes/soldier.cc'
--- src/logic/map_objects/tribes/soldier.cc	2018-07-13 10:35:16 +0000
+++ src/logic/map_objects/tribes/soldier.cc	2018-08-24 18:28:37 +0000
@@ -439,7 +439,6 @@
  * 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-04-07 16:59:00 +0000
+++ src/logic/map_objects/tribes/soldier.h	2018-08-24 18:28:37 +0000
@@ -209,7 +209,6 @@
 
 	/// 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-08-09 11:11:15 +0000
+++ src/logic/map_objects/tribes/worker.cc	2018-08-24 18:28:37 +0000
@@ -2990,7 +2990,6 @@
  * 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-07-26 11:28:05 +0000
+++ src/logic/map_objects/tribes/worker.h	2018-08-24 18:28:37 +0000
@@ -182,7 +182,6 @@
 	                        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-07-29 11:27:33 +0000
+++ src/wui/interactive_base.cc	2018-08-24 18:28:37 +0000
@@ -223,7 +223,7 @@
 				     player.is_hostile(*productionsite->get_owner())))
 					return set_tooltip("");
 			}
-			set_tooltip(productionsite->info_string(Widelands::Building::InfoStringFormat::kTooltip));
+			set_tooltip(productionsite->info_string(Widelands::MapObject::InfoStringType::kTooltip));
 			return;
 		}
 	set_tooltip("");

=== modified file 'src/wui/interactive_base.h'
--- src/wui/interactive_base.h	2018-08-14 13:40:46 +0000
+++ src/wui/interactive_base.h	2018-08-24 18:28:37 +0000
@@ -47,6 +47,16 @@
 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-08-14 13:40:46 +0000
+++ src/wui/interactive_gamebase.cc	2018-08-24 18:28:37 +0000
@@ -54,6 +54,38 @@
 	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,
@@ -137,6 +169,28 @@
 	}
 }
 
+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-08-14 13:40:46 +0000
+++ src/wui/interactive_gamebase.h	2018-08-24 18:28:37 +0000
@@ -99,6 +99,8 @@
 	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-07-28 07:43:51 +0000
+++ src/wui/interactive_player.cc	2018-08-24 18:28:37 +0000
@@ -85,41 +85,18 @@
 	return brightness / 255.;
 }
 
-void draw_immovables_for_visible_field(const Widelands::EditorGameBase& egbase,
+// 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,
-                                       const TextToDraw text_to_draw,
-                                       const Widelands::Player& player,
+									   Widelands::BaseImmovable* const imm,
                                        RenderTarget* dst) {
-	Widelands::BaseImmovable* const imm = field.fcoords.field->get_immovable();
 	if (imm != nullptr && imm->get_positions(egbase).front() == field.fcoords) {
-		TextToDraw draw_text_for_this_immovable = text_to_draw;
-		const Widelands::Player* owner = imm->get_owner();
-		if (owner != nullptr && !player.see_all() && player.is_hostile(*owner)) {
-			draw_text_for_this_immovable =
-			   static_cast<TextToDraw>(draw_text_for_this_immovable & ~TextToDraw::kStatistics);
-		}
 		imm->draw(
-		   egbase.get_gametime(), draw_text_for_this_immovable, 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()) {
-		TextToDraw draw_text_for_this_bob = text_to_draw;
-		const Widelands::Player* owner = bob->get_owner();
-		if (owner != nullptr && !player.see_all() && player.is_hostile(*owner)) {
-			draw_text_for_this_bob =
-			   static_cast<TextToDraw>(draw_text_for_this_bob & ~TextToDraw::kStatistics);
-		}
-		bob->draw(egbase, draw_text_for_this_bob, field.rendertarget_pixel, scale, dst);
-	}
+		   egbase.get_gametime(), field.rendertarget_pixel, scale, dst);
+		return true;
+	}
+	return false;
 }
 
 void draw_immovables_for_formerly_visible_field(const FieldsToDraw::Field& field,
@@ -290,6 +267,9 @@
 	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) {
 		auto* f = fields_to_draw->mutable_field(idx);
@@ -308,8 +288,6 @@
 			}
 		}
 
-		const float scale = 1.f / given_map_view->view().zoom;
-
 		// Add road building overlays if applicable.
 		if (f->vision > 0) {
 			const auto it = road_building.road_previews.find(f->fcoords);
@@ -321,9 +299,16 @@
 
 			// Render stuff that belongs to the node.
 			if (f->vision > 1) {
-				const auto text_to_draw = get_text_to_draw();
-				draw_immovables_for_visible_field(gbase, *f, scale, text_to_draw, plr, dst);
-				draw_bobs_for_visible_field(gbase, *f, scale, text_to_draw, plr, dst);
+				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));
+				}
 			} 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);
@@ -365,6 +350,8 @@
 			}
 		}
 	}
+	// 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-07-22 19:52:53 +0000
+++ src/wui/interactive_spectator.cc	2018-08-24 18:28:37 +0000
@@ -122,6 +122,8 @@
 
 	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);
 
@@ -129,12 +131,14 @@
 
 		Widelands::BaseImmovable* const imm = field.fcoords.field->get_immovable();
 		if (imm != nullptr && imm->get_positions(the_game).front() == field.fcoords) {
-			imm->draw(gametime, text_to_draw, field.rendertarget_pixel, scale, dst);
+			imm->draw(gametime, field.rendertarget_pixel, scale, dst);
+			mapobjects_to_draw_text_for.push_back(std::make_pair(field.rendertarget_pixel.cast<int>(), imm));
 		}
 
 		for (Widelands::Bob* bob = field.fcoords.field->get_first_bob(); bob;
 		     bob = bob->get_next_bob()) {
-			bob->draw(the_game, text_to_draw, field.rendertarget_pixel, scale, dst);
+			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));
 		}
 
 		// Draw work area previews.
@@ -167,6 +171,9 @@
 			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-04-07 16:59:00 +0000
+++ src/wui/transport_draw.cc	2018-08-24 18:28:37 +0000
@@ -27,7 +27,6 @@
 namespace Widelands {
 
 void Flag::draw(uint32_t gametime,
-                const TextToDraw,
                 const Vector2f& point_on_dst,
                 float scale,
                 RenderTarget* dst) {
@@ -54,6 +53,6 @@
 }
 
 /** The road is drawn by the terrain renderer via marked fields. */
-void Road::draw(uint32_t, const TextToDraw, const Vector2f&, float, RenderTarget*) {
+void Road::draw(uint32_t, const Vector2f&, float, RenderTarget*) {
 }
 }


Follow ups