← Back to team overview

widelands-dev team mailing list archive

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

 

cghislai has proposed merging lp:~widelands-dev/widelands/ui_improvments into lp:widelands.

Requested reviews:
  Widelands Developers (widelands-dev)
Related bugs:
  Bug #657285 in widelands: "Multiple tooltips may be shown when opening building information"
  https://bugs.launchpad.net/widelands/+bug/657285
  Bug #738643 in widelands: "Pause game while in 'save'-dialog"
  https://bugs.launchpad.net/widelands/+bug/738643
  Bug #738895 in widelands: "Show a message when the game is autosaving"
  https://bugs.launchpad.net/widelands/+bug/738895
  Bug #744749 in widelands: "Training sites should either show statistics as a military or as a productionsite"
  https://bugs.launchpad.net/widelands/+bug/744749
  Bug #763567 in widelands: "Sort Messages in Message Inbox to be most recent on top"
  https://bugs.launchpad.net/widelands/+bug/763567
  Bug #898129 in widelands: "Workarea color policy"
  https://bugs.launchpad.net/widelands/+bug/898129
  Bug #986526 in widelands: "Clarify "X player teams" map filter"
  https://bugs.launchpad.net/widelands/+bug/986526
  Bug #1132469 in widelands: "List of workers in building window not updating properly"
  https://bugs.launchpad.net/widelands/+bug/1132469
  Bug #1132476 in widelands: "change yellow color in white(?) - building menu % is unreadable"
  https://bugs.launchpad.net/widelands/+bug/1132476

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/ui_improvments/+merge/175090

Some fixes and features related to ui. See linked bugs and commit messages for details.
I tried to make commits as atomic as possible, so they could be merged separately if needed.
-- 
https://code.launchpad.net/~widelands-dev/widelands/ui_improvments/+merge/175090
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/ui_improvments into lp:widelands.
=== added file 'pics/workarea12.png'
Binary files pics/workarea12.png	1970-01-01 00:00:00 +0000 and pics/workarea12.png	2013-07-16 17:39:34 +0000 differ
=== added file 'pics/workarea123.png'
Binary files pics/workarea123.png	1970-01-01 00:00:00 +0000 and pics/workarea123.png	2013-07-16 17:39:34 +0000 differ
=== removed file 'pics/workarea1cumulative.png'
Binary files pics/workarea1cumulative.png	2005-12-29 01:10:07 +0000 and pics/workarea1cumulative.png	1970-01-01 00:00:00 +0000 differ
=== added file 'pics/workarea23.png'
Binary files pics/workarea23.png	1970-01-01 00:00:00 +0000 and pics/workarea23.png	2013-07-16 17:39:34 +0000 differ
=== removed file 'pics/workarea2cumulative.png'
Binary files pics/workarea2cumulative.png	2005-12-29 01:10:07 +0000 and pics/workarea2cumulative.png	1970-01-01 00:00:00 +0000 differ
=== removed file 'pics/workarea3cumulative.png'
Binary files pics/workarea3cumulative.png	2009-11-09 19:01:11 +0000 and pics/workarea3cumulative.png	1970-01-01 00:00:00 +0000 differ
=== modified file 'src/chat.h'
--- src/chat.h	2013-02-10 19:36:24 +0000
+++ src/chat.h	2013-07-16 17:39:34 +0000
@@ -106,6 +106,12 @@
 	virtual void send(const std::string &) = 0;
 
 	/**
+	 * Sends the given message to the local player only
+	 * This may be used to display useful log messages.
+	 */
+	virtual void send_local(const std::string &) = 0;
+
+	/**
 	 * \return a (chronological) list of received chat messages.
 	 * This list need not be stable or monotonic. In other words,
 	 * subsequent calls to this functions may return a smaller or

=== modified file 'src/constants.h'
--- src/constants.h	2013-07-13 14:32:49 +0000
+++ src/constants.h	2013-07-16 17:39:34 +0000
@@ -131,7 +131,7 @@
  * C++ is really bad at integer types. For example this constant is not
  * recognized as a valid value of type Workarea_Info::size_type without a cast.
  */
-#define NUMBER_OF_WORKAREA_PICS static_cast<Workarea_Info::size_type>(3)
+#define NUMBER_OF_WORKAREA_PICS static_cast<Workarea_Info::size_type>(6)
 
 /// The size of the transient (i.e. temporary) surfaces in the cache in bytes.
 /// These are all surfaces that are not loaded from disk.

=== modified file 'src/debugconsole.cc'
--- src/debugconsole.cc	2013-02-10 19:36:24 +0000
+++ src/debugconsole.cc	2013-07-16 17:39:34 +0000
@@ -85,6 +85,10 @@
 		it->second(arg);
 	}
 
+	void send_local(const std::string& msg) {
+		send(msg);
+	}
+
 	const std::vector<ChatMessage> & getMessages() const
 	{
 		return messages;

=== modified file 'src/gamecontroller.cc'
--- src/gamecontroller.cc	2012-02-15 21:25:34 +0000
+++ src/gamecontroller.cc	2013-07-16 17:39:34 +0000
@@ -25,8 +25,10 @@
 #include "logic/playercommand.h"
 #include "profile/profile.h"
 #include "wlapplication.h"
+#include "chat.h"
+#include "wui/interactive_player.h"
 
-struct SinglePlayerGameController : public GameController {
+struct SinglePlayerGameController : public GameController, public ChatProvider {
 	SinglePlayerGameController
 		(Widelands::Game &, bool useai, Widelands::Player_Number local);
 	~SinglePlayerGameController();
@@ -42,6 +44,10 @@
 	bool isPaused();
 	void setPaused(bool paused);
 
+	// Chat provider implementation
+	void send(const std::string & msg);
+	void send_local(const std::string & msg);
+	const std::vector<ChatMessage> & getMessages() const;
 private:
 	Widelands::Game & m_game;
 	bool m_useai;
@@ -52,13 +58,14 @@
 	uint32_t m_player_cmdserial;
 	Widelands::Player_Number m_local;
 	std::vector<Computer_Player *> m_computerplayers;
+	std::vector<ChatMessage> m_chatmessages;
 };
 
 SinglePlayerGameController::SinglePlayerGameController
 	(Widelands::Game        &       game,
 	 bool                     const useai,
 	 Widelands::Player_Number const local)
-	:
+	: ChatProvider(),
 	m_game            (game),
 	m_useai           (useai),
 	m_lastframe       (WLApplication::get()->get_time()),
@@ -156,10 +163,31 @@
 	m_paused = paused;
 }
 
+void SinglePlayerGameController::send_local(const std::string& msg)
+{
+	ChatMessage c;
+	c.msg = msg;
+	c.time = time(0);
+	m_chatmessages.push_back(c);
+	ChatProvider::send(c);
+}
+
+void SinglePlayerGameController::send(const std::string& msg)
+{
+	log("SinglePlayerGameController:: Cannot send chat messages in single player game!");
+}
+
+const std::vector< ChatMessage >& SinglePlayerGameController::getMessages() const
+{
+	return m_chatmessages;
+}
+
 GameController * GameController::createSinglePlayer
 	(Widelands::Game        &       game,
 	 bool                     const cpls,
 	 Widelands::Player_Number const local)
 {
-	return new SinglePlayerGameController(game, cpls, local);
+	SinglePlayerGameController* spgc =  new SinglePlayerGameController(game, cpls, local);
+	game.get_ipl()->set_chat_provider(*spgc);
+	return spgc;
 }

=== modified file 'src/logic/building.cc'
--- src/logic/building.cc	2013-07-14 10:38:26 +0000
+++ src/logic/building.cc	2013-07-16 17:39:34 +0000
@@ -19,6 +19,7 @@
 
 #include <cstdio>
 #include <sstream>
+#include <boost/foreach.hpp>
 
 #include "upcast.h"
 #include "wexception.h"
@@ -436,6 +437,9 @@
 	}
 
 	PlayerImmovable::cleanup(egbase);
+
+	BOOST_FOREACH(boost::signals::connection& c, options_window_connections)
+		c.disconnect();
 }
 
 
@@ -855,6 +859,7 @@
 			set_seeing(true);
 	}
 	PlayerImmovable::add_worker(worker);
+	workers_changed();
 }
 
 
@@ -862,6 +867,7 @@
 	PlayerImmovable::remove_worker(worker);
 	if (not get_workers().size())
 		set_seeing(false);
+	workers_changed();
 }
 
 /**

=== modified file 'src/logic/building.h'
--- src/logic/building.h	2013-07-14 10:38:26 +0000
+++ src/logic/building.h	2013-07-16 17:39:34 +0000
@@ -34,6 +34,7 @@
 #include <string>
 #include <cstring>
 #include <vector>
+#include <boost/signal.hpp>
 
 namespace UI {class Window;}
 struct BuildingHints;
@@ -223,6 +224,7 @@
 
 	void    add_worker(Worker &);
 	void remove_worker(Worker &);
+	mutable boost::signal<void ()> workers_changed;
 
 	void send_message
 		(Game & game,
@@ -268,6 +270,9 @@
 
 	/// Whether we see our vision_range area based on workers in the building
 	bool m_seeing;
+
+	// Signals connected for the option window
+	std::vector<boost::signals::connection> options_window_connections;
 };
 
 }

=== modified file 'src/logic/production_program.cc'
--- src/logic/production_program.cc	2013-04-27 16:55:46 +0000
+++ src/logic/production_program.cc	2013-07-16 17:39:34 +0000
@@ -1288,7 +1288,7 @@
 			snprintf
 				(ps.m_result_buffer, sizeof(ps.m_result_buffer),
 				 _("No soldier for this training level found!"));
-			return ps.program_end(game, Failed);
+			return ps.program_end(game, Skipped);
 		}
 		if        (attribute == atrHP)      {
 			if ((*it)->get_hp_level()      == level)
@@ -1378,7 +1378,7 @@
 			snprintf
 				(ps.m_result_buffer, sizeof(ps.m_result_buffer),
 				 _("No soldier for this training level found!"));
-			return ps.program_end(game, Failed);
+			return ps.program_end(game, Skipped);
 		}
 		if        (attribute == atrHP)      {
 			if ((*it)->get_hp_level     () == level)

=== modified file 'src/logic/productionsite.cc'
--- src/logic/productionsite.cc	2013-05-25 09:30:35 +0000
+++ src/logic/productionsite.cc	2013-07-16 17:39:34 +0000
@@ -457,7 +457,6 @@
 
 	if (upcast(Game, game, &egbase))
 		try_start_working(*game);
-
 	return 0;
 }
 
@@ -874,6 +873,7 @@
 			for (uint32_t i = descr().nr_working_positions(); i;)
 				m_working_positions[--i].worker->gain_experience(game);
 			m_result_buffer[0] = '\0';
+			Building::workers_changed();
 		}
 		calc_statistics();
 		break;

=== modified file 'src/logic/trainingsite.cc'
--- src/logic/trainingsite.cc	2013-06-16 16:29:48 +0000
+++ src/logic/trainingsite.cc	2013-07-16 17:39:34 +0000
@@ -192,12 +192,7 @@
  */
 std::string TrainingSite::get_statistics_string()
 {
-	if (State * const state = get_state())
-		return state->program->descname();
-	else if (m_result == Completed)
-		return _("Resting");
-	else
-		return _("Not Working");
+	return ProductionSite::get_statistics_string();
 }
 
 /**

=== modified file 'src/network/internet_gaming.cc'
--- src/network/internet_gaming.cc	2013-04-20 20:20:34 +0000
+++ src/network/internet_gaming.cc	2013-07-16 17:39:34 +0000
@@ -768,7 +768,14 @@
 	s.send(m_sock);
 }
 
-
+void InternetGaming::send_local(const std::string& msg)
+{
+	ChatMessage c;
+	c.msg = msg;
+	c.time = time(0);
+	messages.push_back(c);
+	ChatProvider::send(c);
+}
 
 /**
  * \returns the boolean value of a string received from the metaserver.

=== modified file 'src/network/internet_gaming.h'
--- src/network/internet_gaming.h	2013-02-10 19:36:24 +0000
+++ src/network/internet_gaming.h	2013-07-16 17:39:34 +0000
@@ -114,6 +114,9 @@
 	// ChatProvider: sends a message via the metaserver.
 	void send(const std::string &);
 
+	// ChatProvider: sends local messages
+	void send_local(const std::string &);
+
 	/// ChatProvider: adds the message to the message list and calls parent.
 	void receive(const ChatMessage & msg) {messages.push_back(msg); ChatProvider::send(msg);}
 

=== modified file 'src/network/netclient.cc'
--- src/network/netclient.cc	2013-04-22 20:15:00 +0000
+++ src/network/netclient.cc	2013-07-16 17:39:34 +0000
@@ -628,6 +628,15 @@
 	s.send(d->sock);
 }
 
+void NetClient::send_local(const std::string& msg)
+{
+	ChatMessage c;
+	c.msg = msg;
+	c.time = time(0);
+	d->chatmessages.push_back(c);
+	ChatProvider::send(c);
+}
+
 const std::vector<ChatMessage> & NetClient::getMessages() const
 {
 	return d->chatmessages;

=== modified file 'src/network/netclient.h'
--- src/network/netclient.h	2013-02-10 19:36:24 +0000
+++ src/network/netclient.h	2013-07-16 17:39:34 +0000
@@ -92,6 +92,7 @@
 
 	// ChatProvider interface
 	void send(const std::string & msg);
+	void send_local(const std::string & msg);
 	const std::vector<ChatMessage> & getMessages() const;
 
 private:

=== modified file 'src/network/nethost.cc'
--- src/network/nethost.cc	2013-07-15 05:18:12 +0000
+++ src/network/nethost.cc	2013-07-16 17:39:34 +0000
@@ -498,6 +498,13 @@
 		h->send(c);
 	}
 
+	void send_local(const std::string & msg) {
+		ChatMessage c;
+		c.time = time(0);
+		c.msg = msg;
+		ChatProvider::send(c);
+	}
+
 	const std::vector<ChatMessage> & getMessages() const {
 		return messages;
 	}

=== modified file 'src/save_handler.cc'
--- src/save_handler.cc	2013-07-14 11:48:13 +0000
+++ src/save_handler.cc	2013-07-16 17:39:34 +0000
@@ -25,6 +25,8 @@
 #include "io/filesystem/filesystem.h"
 #include "game_io/game_saver.h"
 #include "profile/profile.h"
+#include "wui/interactive_player.h"
+#include "chat.h"
 
 #include "log.h"
 
@@ -51,7 +53,10 @@
 	if (elapsed < autosaveInterval)
 		return;
 
-	log("Autosave: interval elapsed (%d s), saving\n", elapsed);
+	// TODO: defer saving to next tick so that this message is shown
+	// before the actual save, or put the saving logic in another thread
+	game.get_ipl()->get_chat_provider()->send_local
+		(_("Saving game..."));
 
 	// save the game
 	std::string complete_filename =
@@ -70,6 +75,8 @@
 	static std::string error;
 	if (!save_game(game, complete_filename, &error)) {
 		log("Autosave: ERROR! - %s\n", error.c_str());
+		game.get_ipl()->get_chat_provider()->send_local
+			(_("Saving failed!"));
 
 		// if backup file was created, move it back
 		if (backup_filename.length() > 0) {
@@ -88,6 +95,8 @@
 	}
 
 	log("Autosave: save took %d ms\n", m_last_saved_time - realtime);
+	game.get_ipl()->get_chat_provider()->send_local
+		(_("Game saved"));
 }
 
 /**
@@ -98,7 +107,6 @@
 		return;
 
 	m_last_saved_time = currenttime;
-	log("Autosave: initialized\n");
 	m_initialized = true;
 }
 

=== modified file 'src/timestring.cc'
--- src/timestring.cc	2013-03-21 10:45:51 +0000
+++ src/timestring.cc	2013-07-16 17:39:34 +0000
@@ -75,3 +75,18 @@
 	}
 	return timestring_buffer;
 }
+
+char gamestringbuffer[] = "000:00:00";
+char * gametimestring(uint32_t gametime)
+{
+	uint32_t time = gametime / 1000;
+	gamestringbuffer[8] = '0' +  time        % 10;
+	gamestringbuffer[7] = '0' + (time /= 10) %  6;
+	gamestringbuffer[5] = '0' + (time /=  6) % 10;
+	gamestringbuffer[4] = '0' + (time /= 10) %  6;
+	gamestringbuffer[2] = '0' + (time /=  6) % 10;
+	gamestringbuffer[1] = '0' + (time /= 10) % 10;
+	gamestringbuffer[0] = '0' + (time /= 10);
+	return gamestringbuffer;
+}
+

=== modified file 'src/timestring.h'
--- src/timestring.h	2012-02-15 21:25:34 +0000
+++ src/timestring.h	2013-07-16 17:39:34 +0000
@@ -21,3 +21,8 @@
 /// seconds since the Epoch). The return value points to a statically allocated
 /// string which might be overwritten by subsequent calls.
 char * timestring();
+
+/// Get a string representation of the game time
+/// as hhh:mm:ss. If Time represents more than
+/// 999 hours, it wraps around
+char * gametimestring(uint32_t gametime);

=== modified file 'src/ui_basic/progressbar.cc'
--- src/ui_basic/progressbar.cc	2012-02-15 21:25:34 +0000
+++ src/ui_basic/progressbar.cc	2013-07-16 17:39:34 +0000
@@ -21,10 +21,12 @@
 
 #include "constants.h"
 #include "graphic/font.h"
-#include "graphic/font_handler.h"
+#include "graphic/font_handler1.h"
+#include "text_layout.h"
 #include "graphic/rendertarget.h"
 
 #include <cstdio>
+#include <boost/format.hpp>
 
 
 namespace UI {
@@ -103,16 +105,11 @@
 	}
 
 	// Print the state in percent
-	char buffer[30];
-
-	snprintf
-		(buffer, sizeof(buffer), "%u%%", static_cast<uint32_t>(fraction * 100));
-
-	UI::g_fh->draw_text
-		(dst, UI::TextStyle::ui_small(),
-		 Point(get_w() / 2, get_h() / 2),
-		 buffer,
-		 Align_Center);
+	// TODO use UI_FNT_COLOR_BRIGHT when merged
+	uint32_t percent = static_cast<uint32_t>(fraction * 100);
+	const std::string progress_text =
+		(boost::format("<font color=%1$s>%2$i%%</font>") % "ffffff" % percent).str();
+	const Point pos(get_w() / 2, get_h() / 2);
+	dst.blit(pos, UI::g_fh1->render(as_uifont(progress_text)), CM_Normal, Align_Center);
 }
-
 }

=== modified file 'src/ui_basic/table.cc'
--- src/ui_basic/table.cc	2013-07-13 14:25:41 +0000
+++ src/ui_basic/table.cc	2013-07-16 17:39:34 +0000
@@ -23,11 +23,13 @@
 #include "graphic/font_handler.h"
 #include "graphic/graphic.h"
 #include "graphic/rendertarget.h"
+#include "graphic/font_handler1.h"
 
 #include "button.h"
 #include "mouse_constants.h"
 #include "scrollbar.h"
 #include "wlapplication.h"
+#include "text_layout.h"
 
 #include "container_iterate.h"
 #include <boost/bind.hpp>
@@ -273,21 +275,13 @@
 			const std::string &       entry_string  = er.get_string (i);
 			uint32_t picw = 0;
 			uint32_t pich = 0;
-			uint32_t stringw = 0;
-			uint32_t stringh = g_fh->get_fontheight(m_fontname, m_fontsize);
+
 			if (entry_picture) {
 				picw = entry_picture->width();
 				pich = entry_picture->height();
 			}
-			Point point =
-				Point(curx, y)
-				+
-				Point
-					(alignment & Align_Right   ?  curw - (picw + stringw)  - 1 :
-					 alignment & Align_HCenter ? (curw - (picw + stringw)) / 2 :
-					 1,
-					 0);
-			if (entry_picture)
+			Point point(curx, y);
+			if (entry_picture) {
 				dst.blit
 					(point +
 					 Point
@@ -296,18 +290,13 @@
 					 	  static_cast<int32_t>(pich))
 					 	 / 2),
 					 entry_picture);
+				point.x += picw;
+			}
 
-			UI::g_fh->draw_text
-				(dst,
-				 TextStyle::makebold(Font::get(m_fontname, m_fontsize), er.use_clr ? er.clr : UI_FONT_CLR_FG),
-				 point +
-				 Point
-				 	(picw,
-				 	 (static_cast<int32_t>(lineheight) -
-				 	  static_cast<int32_t>(stringh))
-				 	 / 2),
-				 entry_string,
-				 alignment);
+			const Image* entry_text_im = UI::g_fh1->render(as_uifont(entry_string, m_fontsize));
+			// Crop to column width
+			UI::correct_for_align(alignment, entry_text_im->width(), entry_text_im->height(), &point);
+			dst.blitrect(point, entry_text_im, Rect(0, 0, curw - picw, lineheight));
 
 			curx += curw;
 		}

=== modified file 'src/ui_fsmenu/loadgame.cc'
--- src/ui_fsmenu/loadgame.cc	2013-07-15 05:18:12 +0000
+++ src/ui_fsmenu/loadgame.cc	2013-07-16 17:39:34 +0000
@@ -29,6 +29,7 @@
 #include "log.h"
 #include "logic/game.h"
 #include "ui_basic/messagebox.h"
+#include "timestring.h"
 
 #include <cstdio>
 
@@ -203,15 +204,9 @@
 			m_tamapname.set_text(_(gpdp.get_mapname()));
 		}
 
-		char buf[200];
+		char buf[20];
 		uint32_t gametime = gpdp.get_gametime();
-
-		int32_t hours = gametime / 3600000;
-		gametime -= hours * 3600000;
-		int32_t minutes = gametime / 60000;
-
-		sprintf(buf, "%02i:%02i", hours, minutes);
-		m_tagametime.set_text(buf);
+		m_tagametime.set_text(gametimestring(gametime));
 
 		sprintf(buf, "%i", gpdp.get_player_nr());
 		m_ta_players.set_text(buf);

=== modified file 'src/ui_fsmenu/loadreplay.cc'
--- src/ui_fsmenu/loadreplay.cc	2013-07-14 16:11:41 +0000
+++ src/ui_fsmenu/loadreplay.cc	2013-07-16 17:39:34 +0000
@@ -28,6 +28,7 @@
 #include "logic/game.h"
 #include "logic/replay.h"
 #include "ui_basic/messagebox.h"
+#include "timestring.h"
 
 Fullscreen_Menu_LoadReplay::Fullscreen_Menu_LoadReplay() :
 	Fullscreen_Menu_Base("choosemapmenu.jpg"),
@@ -186,15 +187,9 @@
 		m_delete.set_enabled(true);
 		m_tamapname.set_text(gpdp.get_mapname());
 
-		char buf[200];
+		char buf[20];
 		uint32_t gametime = gpdp.get_gametime();
-
-		int32_t hours = gametime / 3600000;
-		gametime -= hours * 3600000;
-		int32_t minutes = gametime / 60000;
-
-		sprintf(buf, "%02i:%02i", hours, minutes);
-		m_tagametime.set_text(buf);
+		m_tagametime.set_text(gametimestring(gametime));
 
 		sprintf(buf, "%i", gpdp.get_player_nr());
 		m_ta_players.set_text(buf);

=== modified file 'src/ui_fsmenu/mapselect.cc'
--- src/ui_fsmenu/mapselect.cc	2013-06-15 14:35:17 +0000
+++ src/ui_fsmenu/mapselect.cc	2013-07-16 17:39:34 +0000
@@ -172,11 +172,11 @@
 	vbox->set_size(get_w(), 25);
 	vbox = new UI::Box(this, m_table.get_x(), m_table.get_y() - 60, UI::Box::Horizontal, m_table.get_w());
 	_add_tag_checkbox(vbox, "1v1", _("1v1"));
-	_add_tag_checkbox(vbox, "2teams", _("2 Player Teams"));
-	_add_tag_checkbox(vbox, "3teams", _("3 Player Teams"));
+	_add_tag_checkbox(vbox, "2teams", _("Teams of 2"));
+	_add_tag_checkbox(vbox, "3teams", _("Teams of 3"));
 	vbox->set_size(get_w(), 25);
 	vbox = new UI::Box(this, m_table.get_x(), m_table.get_y() - 30, UI::Box::Horizontal, m_table.get_w());
-	_add_tag_checkbox(vbox, "4teams", _("4 Player Teams"));
+	_add_tag_checkbox(vbox, "4teams", _("Teams of 4"));
 	_add_tag_checkbox(vbox, "ffa", _("Free for all"));
 	_add_tag_checkbox(vbox, "unbalanced", _("Unbalanced"));
 	vbox->set_size(get_w(), 25);

=== modified file 'src/wlapplication.cc'
--- src/wlapplication.cc	2013-07-15 05:18:12 +0000
+++ src/wlapplication.cc	2013-07-16 17:39:34 +0000
@@ -2036,9 +2036,14 @@
 		}
 	} else { // normal singleplayer
 		uint8_t const pn = sp.settings().playernum + 1;
-		boost::scoped_ptr<GameController> ctrl
-			(GameController::createSinglePlayer(game, true, pn));
 		try {
+			// Game controller needs the ibase pointer to init
+			// the chat
+			game.set_ibase
+				(new Interactive_Player
+					(game, g_options.pull_section("global"), pn, false, false));
+			boost::scoped_ptr<GameController> ctrl
+				(GameController::createSinglePlayer(game, true, pn));
 			UI::ProgressWindow loaderUI;
 			std::vector<std::string> tipstext;
 			tipstext.push_back("general_game");
@@ -2052,9 +2057,6 @@
 			loaderUI.step(_("Preparing game"));
 
 			game.set_game_controller(ctrl.get());
-			game.set_ibase
-				(new Interactive_Player
-				 	(game, g_options.pull_section("global"), pn, false, false));
 			game.init_newgame(&loaderUI, sp.settings());
 			game.run(&loaderUI, Widelands::Game::NewNonScenario);
 		} catch (const std::exception & e) {

=== modified file 'src/wui/building_ui.cc'
--- src/wui/building_ui.cc	2013-07-09 05:53:39 +0000
+++ src/wui/building_ui.cc	2013-07-16 17:39:34 +0000
@@ -17,6 +17,8 @@
  *
  */
 
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
 #include "buildingwindow.h"
 #include "logic/building.h"
 #include "ui_basic/window.h"
@@ -30,14 +32,17 @@
  */
 void Building::show_options(Interactive_GameBase & igbase, bool avoid_fastclick)
 {
+	// Reset tooltip before opening the window
+	igbase.set_tooltip("");
 	if (m_optionswindow) {
 		if (m_optionswindow->is_minimal())
 			m_optionswindow->restore();
 		m_optionswindow->move_to_top();
 	} else {
 		create_options_window(igbase, m_optionswindow);
-		if (upcast(Building_Window, bw, m_optionswindow))
+		if (upcast(Building_Window, bw, m_optionswindow)) {
 			bw->set_avoid_fastclick(avoid_fastclick);
+		}
 		// Run a first think here so that certain things like caps buttons
 		// get properly initialized
 		m_optionswindow->think();
@@ -49,6 +54,8 @@
  */
 void Building::hide_options()
 {
+	BOOST_FOREACH(boost::signals::connection& c, options_window_connections)
+		c.disconnect();
 	delete m_optionswindow;
 	m_optionswindow = NULL;
 }

=== modified file 'src/wui/buildingwindow.cc'
--- src/wui/buildingwindow.cc	2013-07-13 15:58:52 +0000
+++ src/wui/buildingwindow.cc	2013-07-16 17:39:34 +0000
@@ -79,13 +79,6 @@
 	set_center_panel(vbox);
 	set_think(true);
 
-	char filename[] = "pics/workarea0cumulative.png";
-	compile_assert(NUMBER_OF_WORKAREA_PICS <= 9);
-	for (Workarea_Info::size_type i = 0; i < NUMBER_OF_WORKAREA_PICS; ++i) {
-		++filename[13];
-		workarea_cumulative_pic[i] = g_gr->images().get(filename);
-	}
-
 	show_workarea();
 
 	set_fastclick_panel(this);
@@ -327,7 +320,7 @@
 				(capsbuttons, "workarea",
 				 0, 0, 34, 34,
 				 g_gr->images().get("pics/but4.png"),
-				 g_gr->images().get("pics/workarea3cumulative.png"),
+				 g_gr->images().get("pics/workarea123.png"),
 				 _("Hide workarea"));
 			m_toggle_workarea->sigclicked.connect
 				(boost::bind(&Building_Window::toggle_workarea, boost::ref(*this)));
@@ -512,41 +505,11 @@
  */
 void Building_Window::show_workarea()
 {
-	if (m_workarea_job_id)
+	if (m_workarea_job_id) {
 		return; // already shown, nothing to be done
-
+	}
 	const Workarea_Info & workarea_info = m_building.descr().m_workarea_info;
-	if (workarea_info.size() == 0)
-		return; // building has no workarea
-
-	Widelands::Map & map =
-		ref_cast<const Interactive_GameBase, UI::Panel>(*get_parent()).egbase()
-		.map();
-	Overlay_Manager & overlay_manager = map.overlay_manager();
-	m_workarea_job_id = overlay_manager.get_a_job_id();
-
-	Widelands::HollowArea<> hollow_area
-		(Widelands::Area<>(m_building.get_position(), 0), 0);
-	Workarea_Info::const_iterator it = workarea_info.begin();
-	for
-		(Workarea_Info::size_type i =
-			std::min(workarea_info.size(), NUMBER_OF_WORKAREA_PICS);
-			i;
-			++it)
-	{
-		--i;
-		hollow_area.radius = it->first;
-		Widelands::MapHollowRegion<> mr(map, hollow_area);
-		do
-			overlay_manager.register_overlay
-				(mr.location(),
-					workarea_cumulative_pic[i],
-					0,
-					Point::invalid(),
-					m_workarea_job_id);
-		while (mr.advance(map));
-		hollow_area.hole_radius = hollow_area.radius;
-	}
+	m_workarea_job_id = igbase().show_work_area(workarea_info, m_building.get_position());
 
 	configure_workarea_button();
 }
@@ -557,11 +520,7 @@
 void Building_Window::hide_workarea()
 {
 	if (m_workarea_job_id) {
-		Widelands::Map & map =
-			ref_cast<const Interactive_GameBase, UI::Panel>(*get_parent()).egbase()
-			.map();
-		Overlay_Manager & overlay_manager = map.overlay_manager();
-		overlay_manager.remove_overlay(m_workarea_job_id);
+		igbase().hide_work_area(m_workarea_job_id);
 		m_workarea_job_id = Overlay_Manager::Job_Id::Null();
 
 		configure_workarea_button();

=== modified file 'src/wui/buildingwindow.h'
--- src/wui/buildingwindow.h	2013-07-08 03:35:09 +0000
+++ src/wui/buildingwindow.h	2013-07-16 17:39:34 +0000
@@ -94,7 +94,6 @@
 	bool m_caps_setup;
 
 	Overlay_Manager::Job_Id m_workarea_job_id;
-	const Image* workarea_cumulative_pic[NUMBER_OF_WORKAREA_PICS];
 	bool m_avoid_fastclick;
 };
 

=== modified file 'src/wui/fieldaction.cc'
--- src/wui/fieldaction.cc	2013-02-09 23:36:30 +0000
+++ src/wui/fieldaction.cc	2013-07-16 17:39:34 +0000
@@ -231,7 +231,6 @@
 	bool m_fastclick; // if true, put the mouse over first button in first tab
 	uint32_t m_best_tab;
 	Overlay_Manager::Job_Id m_workarea_preview_job_id;
-	const Image* workarea_cumulative_pic[NUMBER_OF_WORKAREA_PICS];
 
 	/// Variables to use with attack dialog.
 	AttackBox * m_attack_box;
@@ -297,13 +296,6 @@
 
 
 	set_center_panel(&m_tabpanel);
-
-	char filename[] = "pics/workarea0cumulative.png";
-	compile_assert(NUMBER_OF_WORKAREA_PICS <= 9);
-	for (Workarea_Info::size_type i = 0; i < NUMBER_OF_WORKAREA_PICS; ++i) {
-		++filename[13];
-		workarea_cumulative_pic[i] = g_gr->images().get(filename);
-	}
 }
 
 
@@ -856,44 +848,10 @@
 	(const Widelands::Building_Index::value_t idx)
 {
 	if (ibase().m_show_workarea_preview and not m_workarea_preview_job_id) {
-		m_workarea_preview_job_id = m_overlay_manager.get_a_job_id();
-		Widelands::HollowArea<> hollow_area(Widelands::Area<>(m_node, 0), 0);
 		const Workarea_Info & workarea_info =
 			m_plr->tribe().get_building_descr(Widelands::Building_Index(idx))
 			->m_workarea_info;
-		Workarea_Info::const_iterator it = workarea_info.begin();
-		for
-			(Workarea_Info::size_type i =
-			 	std::min(workarea_info.size(), NUMBER_OF_WORKAREA_PICS);
-			 i;
-			 ++it)
-		{
-			--i;
-			hollow_area.radius = it->first;
-			assert(hollow_area.radius);
-			assert(hollow_area.hole_radius < hollow_area.radius);
-			Widelands::MapHollowRegion<> mr(*m_map, hollow_area);
-			do
-				m_overlay_manager.register_overlay
-					(mr.location(),
-					 workarea_cumulative_pic[i],
-					 0,
-					 Point::invalid(),
-					 m_workarea_preview_job_id);
-			while (mr.advance(*m_map));
-			hollow_area.hole_radius = hollow_area.radius;
-		}
-
-#if 0
-		//  This is debug output.
-		//  Improvement suggestion: add to sign explanation window instead.
-		container_iterate_const(Workarea_Info, workarea_info, i) {
-			log("Radius: %i\n", i.current->first);
-			container_iterate_const(std::set<std::string>, i.current->second, j)
-				log("        %s\n", j.current->c_str());
-		}
-#endif
-
+		m_workarea_preview_job_id = ibase().show_work_area(workarea_info, m_node);
 	}
 }
 

=== modified file 'src/wui/game_main_menu_save_game.cc'
--- src/wui/game_main_menu_save_game.cc	2013-07-15 11:01:59 +0000
+++ src/wui/game_main_menu_save_game.cc	2013-07-16 17:39:34 +0000
@@ -28,10 +28,13 @@
 #include "game_io/game_saver.h"
 #include "i18n.h"
 #include "interactive_gamebase.h"
+#include "gamecontroller.h"
 #include "io/filesystem/filesystem.h"
 #include "io/filesystem/layered_filesystem.h"
 #include "logic/game.h"
 #include "profile/profile.h"
+#include "interactive_player.h"
+#include "timestring.h"
 
 using boost::format;
 
@@ -125,6 +128,10 @@
 	}
 
 	m_editbox->focus();
+	if (!parent.game().get_ipl()->is_multiplayer()) {
+		// Pause the game
+		parent.game().gameController()->setPaused(true);
+	}
 }
 
 
@@ -144,20 +151,11 @@
 	m_button_ok->set_enabled(true);
 
 	m_name.set_text(gpdp.get_mapname());
+
+	uint32_t gametime = gpdp.get_gametime();
+	m_gametime.set_text(gametimestring(gametime));
+
 	char buf[200];
-	uint32_t gametime = gpdp.get_gametime();
-#define SPLIT_GAMETIME(unit, factor) \
-   uint32_t const unit = gametime / factor; gametime %= factor;
-	SPLIT_GAMETIME(days, 86400000);
-	SPLIT_GAMETIME(hours, 3600000);
-	SPLIT_GAMETIME(minutes, 60000);
-	SPLIT_GAMETIME(seconds,  1000);
-	sprintf
-		(buf,
-		 _("%02ud%02uh%02u'%02u\"%03u"),
-		 days, hours, minutes, seconds, gametime);
-	m_gametime.set_text(buf);
-
 	sprintf
 		(buf, "%i %s", gpdp.get_player_nr(),
 		 ngettext(_("player"), _("players"), gpdp.get_player_nr()));
@@ -293,6 +291,15 @@
 	}
 }
 
+void Game_Main_Menu_Save_Game::die()
+{
+	UI::UniqueWindow::die();
+	if (!igbase().game().get_ipl()->is_multiplayer()) {
+		igbase().game().gameController()->setPaused(false);
+	}
+}
+
+
 
 struct DeletionMessageBox : public UI::WLMessageBox {
 	DeletionMessageBox

=== modified file 'src/wui/game_main_menu_save_game.h'
--- src/wui/game_main_menu_save_game.h	2013-07-13 15:17:51 +0000
+++ src/wui/game_main_menu_save_game.h	2013-07-16 17:39:34 +0000
@@ -43,7 +43,7 @@
 	void select_by_name(std::string name);
 private:
 	Interactive_GameBase & igbase();
-	void die() {UI::UniqueWindow::die();}
+	void die();
 	void selected      (uint32_t);
 	void double_clicked(uint32_t);
 	void edit_box_changed();

=== modified file 'src/wui/game_message_menu.cc'
--- src/wui/game_message_menu.cc	2013-07-12 15:11:32 +0000
+++ src/wui/game_message_menu.cc	2013-07-16 17:39:34 +0000
@@ -28,6 +28,7 @@
 #include "logic/playercommand.h"
 
 #include "container_iterate.h"
+#include "timestring.h"
 
 using Widelands::Message;
 using Widelands::Message_Id;
@@ -109,6 +110,8 @@
 
 	list->set_column_compare
 		(ColStatus, boost::bind(&GameMessageMenu::status_compare, this, _1, _2));
+	list->set_sort_column(ColTimeSent);
+	list->set_sort_descending(true);
 
 	set_can_focus(true);
 	focus();
@@ -200,19 +203,8 @@
 		 g_gr->images().get(status_picture_filename[message.status()]));
 	er.set_string(ColTitle, message.title());
 
-	uint32_t time = message.sent();
-	char timestring[] = "000:00:00.000";
-	timestring[12] +=  time        % 10;
-	timestring[11] += (time /= 10) % 10;
-	timestring[10] += (time /= 10) % 10;
-	timestring [8] += (time /= 10) % 10;
-	timestring [7] += (time /= 10) %  6;
-	timestring [5] += (time /=  6) % 10;
-	timestring [4] += (time /= 10) %  6;
-	timestring [2] += (time /=  6) % 10;
-	timestring [1] += (time /= 10) % 10;
-	timestring [0] +=  time /= 10;
-	er.set_string(ColTimeSent, time < 10 ? timestring : "-------------");
+	const uint32_t time = message.sent();
+	er.set_string(ColTimeSent, gametimestring(time));
 }
 
 /*

=== modified file 'src/wui/interactive_base.cc'
--- src/wui/interactive_base.cc	2013-06-15 13:38:19 +0000
+++ src/wui/interactive_base.cc	2013-07-16 17:39:34 +0000
@@ -36,6 +36,7 @@
 #include "logic/maptriangleregion.h"
 #include "logic/player.h"
 #include "logic/productionsite.h"
+#include "logic/maphollowregion.h"
 #include "mapviewpixelconstants.h"
 #include "mapviewpixelfunctions.h"
 #include "minimap.h"
@@ -119,6 +120,15 @@
 	//  funny results.
 	m_sel.pic = g_gr->images().get("pics/fsel.png");
 
+	// Load workarea images.
+	// Start at idx 0 for 2 enhancements, idx 3 for 1, idx 5 if none
+	workarea_pics[0] = g_gr->images().get("pics/workarea123.png");
+	workarea_pics[1] = g_gr->images().get("pics/workarea23.png");
+	workarea_pics[2] = g_gr->images().get("pics/workarea3.png");
+	workarea_pics[3] = g_gr->images().get("pics/workarea12.png");
+	workarea_pics[4] = g_gr->images().get("pics/workarea2.png");
+	workarea_pics[5] = g_gr->images().get("pics/workarea1.png");
+
 	m_label_speed.set_visible(false);
 	m_label_speed_shadow.set_visible(false);
 
@@ -229,6 +239,60 @@
 	egbase().map().overlay_manager().show_buildhelp(t);
 }
 
+// Show the given workareas at the given coords and returns the overlay job id associated
+Overlay_Manager::Job_Id Interactive_Base::show_work_area
+	(const Workarea_Info & workarea_info, Widelands::Coords coords)
+{
+	uint8_t workareas_nrs = workarea_info.size();
+	Workarea_Info::size_type wa_index;
+	switch (workareas_nrs) {
+		case 0: return Overlay_Manager::Job_Id::Null(); break; // no workarea
+		case 1: wa_index = 5; break;
+		case 2: wa_index = 3; break;
+		case 3: wa_index = 0; break;
+		default: assert(false); break;
+	}
+	Widelands::Map & map = m_egbase.map();
+	Overlay_Manager & overlay_manager = map.overlay_manager();
+	Overlay_Manager::Job_Id job_id = overlay_manager.get_a_job_id();
+
+	Widelands::HollowArea<> hollow_area(Widelands::Area<>(coords, 0), 0);
+
+	// Iterate through the work areas, from building to its enhancement
+	Workarea_Info::const_iterator it = workarea_info.begin();
+	for (; it != workarea_info.end(); ++it) {
+		assert(wa_index < NUMBER_OF_WORKAREA_PICS);
+		hollow_area.radius = it->first;
+		Widelands::MapHollowRegion<> mr(map, hollow_area);
+		do
+			overlay_manager.register_overlay
+				(mr.location(),
+					workarea_pics[wa_index],
+					0,
+					Point::invalid(),
+					job_id);
+		while (mr.advance(map));
+		wa_index++;
+		hollow_area.hole_radius = hollow_area.radius;
+	}
+	return job_id;
+#if 0
+		//  This is debug output.
+		//  Improvement suggestion: add to sign explanation window instead.
+		container_iterate_const(Workarea_Info, workarea_info, i) {
+			log("Radius: %i\n", i.current->first);
+			container_iterate_const(std::set<std::string>, i.current->second, j)
+				log("        %s\n", j.current->c_str());
+		}
+#endif
+}
+
+void Interactive_Base::hide_work_area(Overlay_Manager::Job_Id job_id) {
+	Widelands::Map & map = m_egbase.map();
+	Overlay_Manager & overlay_manager = map.overlay_manager();
+	overlay_manager.remove_overlay(job_id);
+}
+
 
 /**
  * Called by \ref Game::postload at the end of the game loading

=== modified file 'src/wui/interactive_base.h'
--- src/wui/interactive_base.h	2013-02-10 19:36:24 +0000
+++ src/wui/interactive_base.h	2013-07-16 17:39:34 +0000
@@ -60,6 +60,8 @@
 	virtual void reference_player_tribe(Widelands::Player_Number, const void * const) {}
 
 	bool m_show_workarea_preview;
+	Overlay_Manager::Job_Id show_work_area(const Workarea_Info & workarea_info, Widelands::Coords coords);
+	void hide_work_area(Overlay_Manager::Job_Id job_id);
 
 	//  point of view for drawing
 	virtual Widelands::Player * get_player() const throw () = 0;
@@ -151,6 +153,7 @@
 	Overlay_Manager::Job_Id m_road_buildhelp_overlay_jobid;
 	Widelands::CoordPath  * m_buildroad;         //  path for the new road
 	Widelands::Player_Number m_road_build_player;
+	const Image* workarea_pics[NUMBER_OF_WORKAREA_PICS];
 
 protected:
 	void toggle_minimap();

=== modified file 'src/wui/interactive_player.cc'
--- src/wui/interactive_player.cc	2013-02-21 19:02:21 +0000
+++ src/wui/interactive_player.cc	2013-07-16 17:39:34 +0000
@@ -85,7 +85,8 @@
 	:
 	Interactive_GameBase (_game, global_s),
 	m_auto_roadbuild_mode(global_s.get_bool("auto_roadbuild_mode", true)),
-m_flag_to_connect(Widelands::Coords::Null()),
+	m_flag_to_connect(Widelands::Coords::Null()),
+	m_multiplayer(multiplayer),
 
 // Chat is different, as m_chatProvider needs to be checked when toggling
 // Buildhelp is different as it does not toggle a UniqueWindow
@@ -152,16 +153,16 @@
 	m_toolbar.add(&m_toggle_statistics_menu, UI::Box::AlignLeft);
 	m_toolbar.add(&m_toggle_minimap,         UI::Box::AlignLeft);
 	m_toolbar.add(&m_toggle_buildhelp,       UI::Box::AlignLeft);
+	// Limit chat width to half the screen, to limit the damage lamers can do
+	// by flooding chat messages
+	m_chatOverlay =
+		new ChatOverlay(this, 10, 25, get_w() / 2, get_h() - 25);
 	if (multiplayer) {
 		m_toolbar.add(&m_toggle_chat,            UI::Box::AlignLeft);
-		// Limit chat width to half the screen, to limit the damage lamers can do
-		// by flooding chat messages
-		m_chatOverlay =
-			new ChatOverlay(this, 10, 25, get_w() / 2, get_h() - 25);
 		m_toggle_chat.set_visible(false);
 		m_toggle_chat.set_enabled(false);
-	} else
-		m_toggle_chat.set_visible(false);
+	}
+
 	m_toolbar.add(&m_toggle_help,            UI::Box::AlignLeft);
 	if (not scenario)
 		m_toggle_objectives.set_visible(false);
@@ -283,8 +284,10 @@
 			m_flag_to_connect = Widelands::Coords::Null();
 		}
 	}
-	m_toggle_chat.set_visible(m_chatenabled);
-	m_toggle_chat.set_enabled(m_chatenabled);
+	if (m_multiplayer) {
+		m_toggle_chat.set_visible(m_chatenabled);
+		m_toggle_chat.set_enabled(m_chatenabled);
+	}
 	{
 		char         buffer[128];
 		char const * msg_icon    = "pics/menu_toggle_oldmessage_menu.png";
@@ -452,7 +455,7 @@
 
 		case SDLK_KP_ENTER:
 		case SDLK_RETURN:
-			if (!m_chatProvider | !m_chatenabled)
+			if (!m_chatProvider | !m_chatenabled || !m_multiplayer)
 				break;
 
 			if (!m_chat.window)

=== modified file 'src/wui/interactive_player.h'
--- src/wui/interactive_player.h	2013-02-10 19:36:24 +0000
+++ src/wui/interactive_player.h	2013-07-16 17:39:34 +0000
@@ -88,12 +88,15 @@
 
 	void popup_message(Widelands::Message_Id, const Widelands::Message &);
 
+	bool is_multiplayer() {return m_multiplayer;}
+
 private:
 	void cmdSwitchPlayer(const std::vector<std::string> & args);
 
 	Widelands::Player_Number m_player_number;
 	bool                     m_auto_roadbuild_mode;
 	Widelands::Coords        m_flag_to_connect;
+	bool                     m_multiplayer;
 
 	UI::Button m_toggle_chat;
 	UI::Button m_toggle_options_menu;

=== modified file 'src/wui/productionsitewindow.cc'
--- src/wui/productionsitewindow.cc	2013-02-10 19:36:24 +0000
+++ src/wui/productionsitewindow.cc	2013-07-16 17:39:34 +0000
@@ -105,13 +105,41 @@
 			 worker_box,
 			 productionsite().descr().nr_working_positions() > 1 ?
 			 _("Workers") : _("Worker"));
+		update_worker_table();
 	}
 }
 
 void ProductionSite_Window::think()
 {
 	Building_Window::think();
-
+	// If we have pending requests, update table as the worker might be coming
+	for
+		(unsigned int i = 0;
+			i < productionsite().descr().nr_working_positions(); ++i)
+	{
+		if (productionsite().working_positions()[i].worker_request) {
+			update_worker_table();
+			break;
+		}
+	}
+}
+
+/*
+===============
+Create the production site information window.
+===============
+*/
+void ProductionSite::create_options_window
+	(Interactive_GameBase & parent, UI::Window * & registry)
+{
+	ProductionSite_Window* win = new ProductionSite_Window(parent, *this, registry);
+	options_window_connections.push_back
+		(workers_changed.connect(boost::bind
+			(&ProductionSite_Window::update_worker_table, boost::ref(*win))));
+}
+
+void ProductionSite_Window::update_worker_table()
+{
 	if (m_worker_table) {
 		assert
 			(productionsite().descr().nr_working_positions() ==
@@ -155,7 +183,7 @@
 					er.set_string(1, "---");
 					er.set_string(2, "---");
 				}
-			} else {
+			} else if (request) {
 				const Widelands::Worker_Descr * desc =
 					productionsite().tribe().get_worker_descr(request->get_index());
 				er.set_picture
@@ -164,20 +192,13 @@
 
 				er.set_string(1, "");
 				er.set_string(2, "");
+			} else {
+				// Occurs during cleanup
+				return;
 			}
 		}
 	}
-}
-
-/*
-===============
-Create the production site information window.
-===============
-*/
-void ProductionSite::create_options_window
-	(Interactive_GameBase & parent, UI::Window * & registry)
-{
-	new ProductionSite_Window(parent, *this, registry);
+	m_worker_table->update();
 }
 
 void ProductionSite_Window::evict_worker() {

=== modified file 'src/wui/productionsitewindow.h'
--- src/wui/productionsitewindow.h	2013-02-02 14:42:48 +0000
+++ src/wui/productionsitewindow.h	2013-07-16 17:39:34 +0000
@@ -33,7 +33,7 @@
 	Widelands::ProductionSite & productionsite() {
 		return ref_cast<Widelands::ProductionSite, Widelands::Building>(building());
 	}
-
+	void update_worker_table();
 protected:
 	virtual void think();
 	void evict_worker();


Follow ups