Merge lp:~widelands-dev/widelands/log_messages into lp:widelands

Proposed by cghislai
Status: Merged
Merged at revision: 6727
Proposed branch: lp:~widelands-dev/widelands/log_messages
Merge into: lp:widelands
Diff against target: 1271 lines (+343/-161)
35 files modified
src/chat.cc (+100/-1)
src/chat.h (+1/-6)
src/debugconsole.cc (+0/-4)
src/gamecontroller.cc (+2/-30)
src/log.h (+0/-1)
src/logic/game.cc (+3/-1)
src/logic/game.h (+4/-1)
src/logic/immovable.cc (+1/-0)
src/logic/notification.h (+1/-2)
src/logic/player.cc (+1/-1)
src/logmessage.h (+41/-0)
src/network/internet_gaming.cc (+0/-9)
src/network/internet_gaming.h (+0/-3)
src/network/netclient.cc (+0/-9)
src/network/netclient.h (+1/-1)
src/network/nethost.cc (+0/-7)
src/save_handler.cc (+8/-11)
src/ui_basic/panel.h (+1/-1)
src/ui_basic/unique_window.cc (+2/-2)
src/ui_basic/window.cc (+15/-6)
src/ui_basic/window.h (+1/-0)
src/wlapplication.cc (+3/-1)
src/wui/chatoverlay.cc (+86/-18)
src/wui/chatoverlay.h (+3/-0)
src/wui/game_main_menu_save_game.cc (+11/-9)
src/wui/game_main_menu_save_game.h (+3/-1)
src/wui/gamechatpanel.cc (+2/-1)
src/wui/interactive_base.cc (+14/-0)
src/wui/interactive_base.h (+12/-0)
src/wui/interactive_gamebase.cc (+3/-4)
src/wui/interactive_gamebase.h (+4/-3)
src/wui/interactive_player.cc (+3/-9)
src/wui/interactive_player.h (+0/-3)
src/wui/interactive_spectator.cc (+9/-11)
src/wui/watchwindow.cc (+8/-5)
To merge this branch: bzr merge lp:~widelands-dev/widelands/log_messages
Reviewer Review Type Date Requested Status
SirVer Needs Information
Review via email: mp+177712@code.launchpad.net

Description of the change

This is a rework of the use of chat messages to log informationnal messages.

- A new LogMessage struct was created appart of ChatMessage.
- The interactivegamebase is a NoteSender of LogMessage. His log_message() method will send the message to all receivers. The ChatOverlay has also moved in a protected field in this class. This was required so it is initialized before the toolbar box, so that chat messages are displayed below it.
- The ChatOverlay receives ChatMessage as well as LogMessage notes. It keeps the log message in a local vector. It displays both chat and logs mixed and in chronological order. It also uses the new renderer to display them.
- All send_local methods are removed, and it was quite a hack anyway. Several classes do not extend ChatProvider anymore.

Some things to review:
- bug 1206712 can be triggered quite easily using this branch.
- I didn't manage to draw a semi-transparent background below chat messages, it doesn't seem to work as expected?

To post a comment you must log in.
Revision history for this message
cghislai (charlyghislain) wrote :

So I've added some code dealing with autosaving , the watch window and replays. Sorry for that, but I felt like rewriting the same things over and over.

The additional changes:
- Add a multiplayer field and getter in interactive gamebase
- Make Panel::die virtual and overwrite in Window to close it and in the save dialog to unpause the game
- Do not rely on interactive player in some place (watch window, save handler)
- Prevent autosaving in replay and prevent autopausing in multiplayer.

Revision history for this message
SirVer (sirver) wrote :

Changes sound nice, maybe I can get it some more code reviews tonight before bed and after work.

Revision history for this message
SirVer (sirver) wrote :

I'll definitely review this this week - I am then all caught up on reviews afaik.

Revision history for this message
SirVer (sirver) wrote :

Review done. Very little comments - looks very good to me. Makes the code much easier to understand and gets rid of a lot of redundancies. I wondered if it would not be possible to do much of what you've done with composition instead of inheritance though. The multiple inheritance we have all of the place there is really horrible.

One approach could also be boost::signals to install handlers for chat messages and then there are either set or not. What do you think about this?

Feel free to merge when you are happy with this though - I will not review much code in the next two weeks.

review: Needs Information
Revision history for this message
cghislai (charlyghislain) wrote :

I completely agree with using composition over inheritance. However, the inheritance you pointed here were not completely related to the log_message thing and it requires more refactoring than expected to change them.
So I think I will open a bug report so that I won't forget to check that later, and that I will merge this as it will allow me to get a bit further with the fh1 branch.

Revision history for this message
SirVer (sirver) wrote :

perfectly acceptable solution for now. This branch made the code nicer to look at and easier to understand for sure. Thanks for your work as always!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/chat.cc'
2--- src/chat.cc 2013-07-26 05:57:03 +0000
3+++ src/chat.cc 2013-08-21 08:32:15 +0000
4@@ -23,7 +23,7 @@
5
6 using namespace Widelands;
7
8-std::string ChatMessage::toPrintable() const
9+std::string ChatMessage::toOldRichText() const
10 {
11 std::string message = "<p font-color=#33ff33 font-size=9>";
12
13@@ -120,6 +120,105 @@
14 return message + "<br></p>";
15 }
16
17+std::string ChatMessage::toPrintable() const
18+{
19+ std::string message = "<p><font color=33ff33 size=9>";
20+
21+ // Escape richtext characters
22+ // The goal of this code is two-fold:
23+ // 1. Assuming an honest game host, we want to prevent the ability of
24+ // clients to use richtext.
25+ // 2. Assuming a malicious host or meta server, we want to reduce the
26+ // likelihood that a bug in the richtext renderer can be exploited,
27+ // by restricting the set of allowed richtext commands.
28+ // Most notably, images are not allowed in richtext at all.
29+ //
30+ // Note that we do want host and meta server to send some richtext code,
31+ // as the ability to send formatted commands is nice for the usability
32+ // of meta server and dedicated servers, so we're treading a bit of a
33+ // fine line here.
34+ std::string sanitized;
35+ for (std::string::size_type pos = 0; pos < msg.size(); ++pos) {
36+ if (msg[pos] == '<') {
37+ if (playern < 0) {
38+ static const std::string good1 = "</p><p";
39+ static const std::string good2 = "<br>";
40+ if (!msg.compare(pos, good1.size(), good1)) {
41+ std::string::size_type nextclose = msg.find('>', pos + good1.size());
42+ if
43+ (nextclose != std::string::npos &&
44+ (nextclose == pos + good1.size() || msg[pos + good1.size()] == ' '))
45+ {
46+ sanitized += good1;
47+ pos += good1.size() - 1;
48+ continue;
49+ }
50+ } else if (!msg.compare(pos, good2.size(), good2)) {
51+ sanitized += good2;
52+ pos += good2.size() - 1;
53+ continue;
54+ }
55+ }
56+
57+ sanitized += "&lt;";
58+ } else {
59+ sanitized += msg[pos];
60+ }
61+ }
62+
63+ // time calculation
64+ char ts[13];
65+ strftime(ts, sizeof(ts), "[%H:%M] ", localtime(&time));
66+ message += ts;
67+
68+ message += "</font><font size=14 face=DejaVuSerif color=";
69+ message += color();
70+
71+ if (recipient.size() && sender.size()) {
72+ // Personal message handling
73+ if (sanitized.compare(0, 3, "/me")) {
74+ message += " bold=1>";
75+ message += sender;
76+ message += " @ ";
77+ message += recipient;
78+ message += ":</font><font size=14 face=DejaVuSerif shadow=1 color=eeeeee> ";
79+ message += sanitized;
80+ } else {
81+ message += ">@";
82+ message += recipient;
83+ message += " \\> </font><font size=14";
84+ message += " face=DejaVuSerif color=";
85+ message += color();
86+ message += " italic=1 shadow=1> ";
87+ message += sender;
88+ message += sanitized.substr(3);
89+ }
90+ } else {
91+ // Normal messages handling
92+ if (not sanitized.compare(0, 3, "/me")) {
93+ message += " italic=1>-\\> ";
94+ if (sender.size())
95+ message += sender;
96+ else
97+ message += "***";
98+ message += sanitized.substr(3);
99+ } else if (sender.size()) {
100+ message += " bold=1>";
101+ message += sender;
102+ message += ":</font><font size=14 face=DejaVuSerif shadow=1 color=eeeeee> ";
103+ message += sanitized;
104+ } else {
105+ message += " bold=1>*** ";
106+ message += sanitized;
107+ }
108+ }
109+
110+ // return the formated message
111+ return message + "</font><br></p>";
112+}
113+
114+
115+
116 std::string ChatMessage::toPlainString() const
117 {
118 return sender + ": " + msg;
119
120=== modified file 'src/chat.h'
121--- src/chat.h 2013-07-26 19:16:51 +0000
122+++ src/chat.h 2013-08-21 08:32:15 +0000
123@@ -73,6 +73,7 @@
124 * \return a richtext string that can be displayed to the user.
125 */
126 std::string toPrintable() const;
127+ std::string toOldRichText() const;
128
129 /**
130 * \return a plain string containing the sender + message.
131@@ -106,12 +107,6 @@
132 virtual void send(const std::string &) = 0;
133
134 /**
135- * Sends the given message to the local player only
136- * This may be used to display useful log messages.
137- */
138- virtual void send_local(const std::string &) = 0;
139-
140- /**
141 * \return a (chronological) list of received chat messages.
142 * This list need not be stable or monotonic. In other words,
143 * subsequent calls to this functions may return a smaller or
144
145=== modified file 'src/debugconsole.cc'
146--- src/debugconsole.cc 2013-07-26 19:16:51 +0000
147+++ src/debugconsole.cc 2013-08-21 08:32:15 +0000
148@@ -86,10 +86,6 @@
149 it->second(arg);
150 }
151
152- void send_local(const std::string& msg) {
153- send(msg);
154- }
155-
156 const std::vector<ChatMessage> & getMessages() const
157 {
158 return messages;
159
160=== modified file 'src/gamecontroller.cc'
161--- src/gamecontroller.cc 2013-08-01 10:51:41 +0000
162+++ src/gamecontroller.cc 2013-08-21 08:32:15 +0000
163@@ -19,7 +19,6 @@
164
165 #include "gamecontroller.h"
166
167-#include "chat.h"
168 #include "computer_player.h"
169 #include "logic/game.h"
170 #include "logic/player.h"
171@@ -27,9 +26,8 @@
172 #include "logic/playersmanager.h"
173 #include "profile/profile.h"
174 #include "wlapplication.h"
175-#include "wui/interactive_player.h"
176
177-struct SinglePlayerGameController : public GameController, public ChatProvider {
178+struct SinglePlayerGameController : public GameController {
179 SinglePlayerGameController
180 (Widelands::Game &, bool useai, Widelands::Player_Number local);
181 ~SinglePlayerGameController();
182@@ -46,10 +44,6 @@
183 void setPaused(bool paused);
184 void report_result(uint8_t player, Widelands::PlayerEndResult result, const std::string & info);
185
186- // Chat provider implementation
187- void send(const std::string & msg);
188- void send_local(const std::string & msg);
189- const std::vector<ChatMessage> & getMessages() const;
190 private:
191 Widelands::Game & m_game;
192 bool m_useai;
193@@ -60,15 +54,13 @@
194 uint32_t m_player_cmdserial;
195 Widelands::Player_Number m_local;
196 std::vector<Computer_Player *> m_computerplayers;
197- std::vector<ChatMessage> m_chatmessages;
198 };
199
200 SinglePlayerGameController::SinglePlayerGameController
201 (Widelands::Game & game,
202 bool const useai,
203 Widelands::Player_Number const local)
204- : ChatProvider(),
205- m_game (game),
206+ : m_game (game),
207 m_useai (useai),
208 m_lastframe (WLApplication::get()->get_time()),
209 m_time (m_game.get_gametime()),
210@@ -178,31 +170,11 @@
211 m_game.player_manager()->add_player_end_status(pes);
212 }
213
214-void SinglePlayerGameController::send_local(const std::string& msg)
215-{
216- ChatMessage c;
217- c.msg = msg;
218- c.time = time(0);
219- m_chatmessages.push_back(c);
220- ChatProvider::send(c);
221-}
222-
223-void SinglePlayerGameController::send(const std::string& /* msg */)
224-{
225- log("SinglePlayerGameController:: Cannot send chat messages in single player game!");
226-}
227-
228-const std::vector< ChatMessage >& SinglePlayerGameController::getMessages() const
229-{
230- return m_chatmessages;
231-}
232-
233 GameController * GameController::createSinglePlayer
234 (Widelands::Game & game,
235 bool const cpls,
236 Widelands::Player_Number const local)
237 {
238 SinglePlayerGameController* spgc = new SinglePlayerGameController(game, cpls, local);
239- game.get_ipl()->set_chat_provider(*spgc);
240 return spgc;
241 }
242
243=== modified file 'src/log.h'
244--- src/log.h 2013-07-18 06:31:11 +0000
245+++ src/log.h 2013-08-21 08:32:15 +0000
246@@ -53,5 +53,4 @@
247
248 extern bool g_verbose;
249
250-
251 #endif
252
253=== modified file 'src/logic/game.cc'
254--- src/logic/game.cc 2013-08-13 19:43:52 +0000
255+++ src/logic/game.cc 2013-08-21 08:32:15 +0000
256@@ -472,8 +472,10 @@
257 * \note loader_ui can be nullptr, if this is run as dedicated server.
258 */
259 bool Game::run
260- (UI::ProgressWindow * loader_ui, Start_Game_Type const start_game_type)
261+ (UI::ProgressWindow * loader_ui, Start_Game_Type const start_game_type,
262+ bool replay)
263 {
264+ m_replay = replay;
265 postload();
266
267 if (start_game_type != Loaded) {
268
269=== modified file 'src/logic/game.h'
270--- src/logic/game.h 2013-08-07 12:32:36 +0000
271+++ src/logic/game.h 2013-08-21 08:32:15 +0000
272@@ -105,7 +105,7 @@
273 bool run_splayer_scenario_direct(char const * mapname);
274 bool run_load_game (std::string filename);
275 enum Start_Game_Type {NewSPScenario, NewNonScenario, Loaded, NewMPScenario};
276- bool run(UI::ProgressWindow * loader_ui, Start_Game_Type);
277+ bool run(UI::ProgressWindow * loader_ui, Start_Game_Type, bool replay = false);
278
279 virtual void postload();
280
281@@ -190,6 +190,8 @@
282
283 const std::string & get_win_condition_displayname() {return m_win_condition_displayname;}
284
285+ bool is_replay() const {return m_replay;};
286+
287 private:
288 void SyncReset();
289
290@@ -254,6 +256,7 @@
291
292 /// For save games and statistics generation
293 std::string m_win_condition_displayname;
294+ bool m_replay;
295 };
296
297 inline Coords Game::random_location(Coords location, uint8_t radius) {
298
299=== modified file 'src/logic/immovable.cc'
300--- src/logic/immovable.cc 2013-08-07 04:01:58 +0000
301+++ src/logic/immovable.cc 2013-08-21 08:32:15 +0000
302@@ -21,6 +21,7 @@
303
304 #include <boost/format.hpp>
305 #include <config.h>
306+
307 #include <cstdio>
308
309 #include "container_iterate.h"
310
311=== modified file 'src/logic/notification.h'
312--- src/logic/notification.h 2013-08-10 16:57:13 +0000
313+++ src/logic/notification.h 2013-08-21 08:32:15 +0000
314@@ -54,8 +54,7 @@
315 (*m_links.rbegin())->disconnect(*this);
316 }
317
318-protected:
319- void send(const T & note) {
320+ void send(const T & note) const {
321 container_iterate_const(Links, m_links, i)
322 (*i.current)->receive(note);
323 }
324
325=== modified file 'src/logic/player.cc'
326--- src/logic/player.cc 2013-08-17 08:39:21 +0000
327+++ src/logic/player.cc 2013-08-21 08:32:15 +0000
328@@ -115,7 +115,7 @@
329 assert(former_buildings && former_buildings->empty());
330 former_buildings->push_back(bi);
331
332- while (true) {
333+ for (;;) {
334 Widelands::Building_Index oldest_idx = former_buildings->front();
335 const Widelands::Building_Descr * oldest = tribe_descr.get_building_descr(oldest_idx);
336 if (!oldest->is_enhanced()) {
337
338=== added file 'src/logmessage.h'
339--- src/logmessage.h 1970-01-01 00:00:00 +0000
340+++ src/logmessage.h 2013-08-21 08:32:15 +0000
341@@ -0,0 +1,41 @@
342+/*
343+ * Copyright (C) 2008-2011 by the Widelands Development Team
344+ *
345+ * This program is free software; you can redistribute it and/or
346+ * modify it under the terms of the GNU General Public License
347+ * as published by the Free Software Foundation; either version 2
348+ * of the License, or (at your option) any later version.
349+ *
350+ * This program is distributed in the hope that it will be useful,
351+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
352+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
353+ * GNU General Public License for more details.
354+ *
355+ * You should have received a copy of the GNU General Public License
356+ * along with this program; if not, write to the Free Software
357+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
358+ *
359+ */
360+
361+#ifndef LOGMESSAGE_H
362+#define LOGMESSAGE_H
363+
364+#include <string>
365+
366+
367+/**
368+ * Represents one log message.
369+ */
370+struct LogMessage {
371+ /**
372+ * The (real-)time at which the message was received.
373+ */
374+ time_t time;
375+
376+ /**
377+ * The actual chat message
378+ */
379+ std::string msg;
380+};
381+
382+#endif
383
384=== modified file 'src/network/internet_gaming.cc'
385--- src/network/internet_gaming.cc 2013-07-29 18:41:11 +0000
386+++ src/network/internet_gaming.cc 2013-08-21 08:32:15 +0000
387@@ -768,15 +768,6 @@
388 s.send(m_sock);
389 }
390
391-void InternetGaming::send_local(const std::string& msg)
392-{
393- ChatMessage c;
394- c.msg = msg;
395- c.time = time(0);
396- messages.push_back(c);
397- ChatProvider::send(c);
398-}
399-
400 /**
401 * \returns the boolean value of a string received from the metaserver.
402 * If conversion fails, it throws a \ref warning
403
404=== modified file 'src/network/internet_gaming.h'
405--- src/network/internet_gaming.h 2013-07-26 20:19:36 +0000
406+++ src/network/internet_gaming.h 2013-08-21 08:32:15 +0000
407@@ -113,9 +113,6 @@
408 // ChatProvider: sends a message via the metaserver.
409 void send(const std::string &);
410
411- // ChatProvider: sends local messages
412- void send_local(const std::string &);
413-
414 /// ChatProvider: adds the message to the message list and calls parent.
415 void receive(const ChatMessage & msg) {messages.push_back(msg); ChatProvider::send(msg);}
416
417
418=== modified file 'src/network/netclient.cc'
419--- src/network/netclient.cc 2013-08-01 10:51:41 +0000
420+++ src/network/netclient.cc 2013-08-21 08:32:15 +0000
421@@ -640,15 +640,6 @@
422 s.send(d->sock);
423 }
424
425-void NetClient::send_local(const std::string& msg)
426-{
427- ChatMessage c;
428- c.msg = msg;
429- c.time = time(0);
430- d->chatmessages.push_back(c);
431- ChatProvider::send(c);
432-}
433-
434 const std::vector<ChatMessage> & NetClient::getMessages() const
435 {
436 return d->chatmessages;
437
438=== modified file 'src/network/netclient.h'
439--- src/network/netclient.h 2013-08-01 10:51:41 +0000
440+++ src/network/netclient.h 2013-08-21 08:32:15 +0000
441@@ -27,6 +27,7 @@
442
443 struct NetClientImpl;
444
445+//FIXME Use composition instead of inheritance
446 /**
447 * NetClient manages the lifetime of a network game in which this computer
448 * participates as a client.
449@@ -94,7 +95,6 @@
450
451 // ChatProvider interface
452 void send(const std::string & msg);
453- void send_local(const std::string & msg);
454 const std::vector<ChatMessage> & getMessages() const;
455
456 private:
457
458=== modified file 'src/network/nethost.cc'
459--- src/network/nethost.cc 2013-08-06 10:40:54 +0000
460+++ src/network/nethost.cc 2013-08-21 08:32:15 +0000
461@@ -498,13 +498,6 @@
462 h->send(c);
463 }
464
465- void send_local(const std::string & msg) {
466- ChatMessage c;
467- c.time = time(0);
468- c.msg = msg;
469- receive(c);
470- }
471-
472 const std::vector<ChatMessage> & getMessages() const {
473 return messages;
474 }
475
476=== modified file 'src/save_handler.cc'
477--- src/save_handler.cc 2013-08-01 10:51:41 +0000
478+++ src/save_handler.cc 2013-08-21 08:32:15 +0000
479@@ -25,9 +25,10 @@
480 #include "log.h"
481 #include "logic/game.h"
482 #include "profile/profile.h"
483+#include "upcast.h"
484 #include "wexception.h"
485 #include "wlapplication.h"
486-#include "wui/interactive_player.h"
487+#include "wui/interactive_base.h"
488
489 using Widelands::Game_Saver;
490
491@@ -41,6 +42,9 @@
492 if (!m_allow_saving) {
493 return;
494 }
495+ if (game.is_replay()) {
496+ return;
497+ }
498
499 if (m_save_requested) {
500 if (!m_save_filename.empty()) {
501@@ -68,10 +72,7 @@
502
503 // TODO: defer saving to next tick so that this message is shown
504 // before the actual save, or put the saving logic in another thread
505- if (game.get_igbase()) {
506- game.get_igbase()->get_chat_provider()->send_local
507- (_("Saving game..."));
508- }
509+ game.get_ibase()->log_message(_("Saving game..."));
510
511 // save the game
512 const std::string complete_filename = create_file_name(get_base_dir(), filename);
513@@ -90,8 +91,7 @@
514 std::string error;
515 if (!save_game(game, complete_filename, &error)) {
516 log("Autosave: ERROR! - %s\n", error.c_str());
517- game.get_ipl()->get_chat_provider()->send_local
518- (_("Saving failed!"));
519+ game.get_ibase()->log_message(_("Saving failed!"));
520
521 // if backup file was created, move it back
522 if (backup_filename.length() > 0) {
523@@ -110,10 +110,7 @@
524 }
525
526 log("Autosave: save took %d ms\n", m_last_saved_time - realtime);
527- if (game.get_igbase()) {
528- game.get_igbase()->get_chat_provider()->send_local
529- (_("Game saved"));
530- }
531+ game.get_ibase()->log_message(_("Game saved"));
532 }
533
534 /**
535
536=== modified file 'src/ui_basic/panel.h'
537--- src/ui_basic/panel.h 2013-08-02 18:36:03 +0000
538+++ src/ui_basic/panel.h 2013-08-21 08:32:15 +0000
539@@ -235,7 +235,7 @@
540 std::string ui_fn();
541
542 protected:
543- void die();
544+ virtual void die();
545 bool keyboard_free() {return !(_focus);}
546
547 virtual void update_desired_size();
548
549=== modified file 'src/ui_basic/unique_window.cc'
550--- src/ui_basic/unique_window.cc 2013-07-26 20:19:36 +0000
551+++ src/ui_basic/unique_window.cc 2013-08-21 08:32:15 +0000
552@@ -42,7 +42,7 @@
553 */
554 void UniqueWindow::Registry::destroy() {
555 if (window) {
556- delete window;
557+ window->die();
558 }
559 }
560
561@@ -51,7 +51,7 @@
562 */
563 void UniqueWindow::Registry::toggle() {
564 if (window) {
565- delete window;
566+ window->die();
567 } else {
568 constr();
569 }
570
571=== modified file 'src/ui_basic/window.cc'
572--- src/ui_basic/window.cc 2013-08-01 14:26:47 +0000
573+++ src/ui_basic/window.cc 2013-08-21 08:32:15 +0000
574@@ -453,12 +453,7 @@
575 grab_mouse(true);
576 } else if (btn == SDL_BUTTON_RIGHT) {
577 play_click();
578- if (is_modal())
579- end_modal(0);
580- else
581- delete this; // is this 100% safe?
582- // FIXME No, at least provide a flag for making a window unclosable and
583- // FIXME provide a callback.
584+ die();
585 }
586
587 return true;
588@@ -490,6 +485,20 @@
589 return true;
590 }
591
592+/**
593+ * Close the window. Overwrite this virtual method if you want
594+ * to take some action before the window is destroyed, or to
595+ * prevent it
596+ */
597+void Window::die()
598+{
599+ if (is_modal()) {
600+ end_modal(0);
601+ } else {
602+ Panel::die();
603+ }
604+}
605+
606
607 void Window::restore() {
608 assert(_is_minimal);
609
610=== modified file 'src/ui_basic/window.h'
611--- src/ui_basic/window.h 2013-07-26 20:19:36 +0000
612+++ src/ui_basic/window.h 2013-08-21 08:32:15 +0000
613@@ -90,6 +90,7 @@
614 bool handle_tooltip();
615
616 protected:
617+ virtual void die();
618 virtual void layout();
619 virtual void update_desired_size();
620
621
622=== modified file 'src/wlapplication.cc'
623--- src/wlapplication.cc 2013-08-02 10:45:32 +0000
624+++ src/wlapplication.cc 2013-08-21 08:32:15 +0000
625@@ -2226,7 +2226,9 @@
626 game.set_write_replay(false);
627 ReplayGameController rgc(game, m_filename);
628
629- game.run(&loaderUI, Widelands::Game::Loaded);
630+ game.save_handler().set_allow_saving(false);
631+
632+ game.run(&loaderUI, Widelands::Game::Loaded, true);
633 } catch (const std::exception & e) {
634 log("Fatal Exception: %s\n", e.what());
635 emergency_save(game);
636
637=== modified file 'src/wui/chatoverlay.cc'
638--- src/wui/chatoverlay.cc 2013-07-26 20:19:36 +0000
639+++ src/wui/chatoverlay.cc 2013-08-21 08:32:15 +0000
640@@ -20,8 +20,9 @@
641 #include "wui/chatoverlay.h"
642
643 #include "chat.h"
644+#include "graphic/font_handler1.h"
645 #include "graphic/rendertarget.h"
646-#include "graphic/richtext.h"
647+#include "logmessage.h"
648 #include "profile/profile.h"
649
650 /**
651@@ -30,7 +31,8 @@
652 static const int32_t CHAT_DISPLAY_TIME = 10;
653 static const uint32_t MARGIN = 2;
654
655-struct ChatOverlay::Impl : Widelands::NoteReceiver<ChatMessage> {
656+struct ChatOverlay::Impl : Widelands::NoteReceiver<ChatMessage>,
657+ Widelands::NoteReceiver<LogMessage> {
658 bool transparent;
659 ChatProvider * chat;
660 bool havemessages;
661@@ -39,12 +41,16 @@
662 time_t oldest;
663
664 /// Layouted message list
665- UI::RichText rt;
666+ std::string all_text;
667+
668+ /// Log messages
669+ std::vector<LogMessage> log_messages;
670
671 Impl() : transparent(false), chat(0), havemessages(false), oldest(0) {}
672
673 void recompute();
674 virtual void receive(const ChatMessage & note);
675+ virtual void receive(const LogMessage & note);
676 };
677
678 ChatOverlay::ChatOverlay
679@@ -56,8 +62,6 @@
680 m->transparent = s.get_bool("transparent_chat", true);
681
682 set_think(true);
683- m->rt.set_width(w - 2 * MARGIN);
684- m->rt.set_background_color(RGBColor(50, 50, 50));
685 }
686
687 ChatOverlay::~ChatOverlay()
688@@ -67,10 +71,20 @@
689 void ChatOverlay::setChatProvider(ChatProvider & chat)
690 {
691 m->chat = &chat;
692- m->connect(chat);
693+ Widelands::NoteReceiver<ChatMessage>* cmr
694+ = dynamic_cast<Widelands::NoteReceiver<ChatMessage>*>(m.get());
695+ cmr->connect(chat);
696 m->recompute();
697 }
698
699+void ChatOverlay::setLogProvider(Widelands::NoteSender<LogMessage>& log_sender)
700+{
701+ Widelands::NoteReceiver<LogMessage>* lmr
702+ = dynamic_cast<Widelands::NoteReceiver<LogMessage>*>(m.get());
703+ lmr->connect(log_sender);
704+}
705+
706+
707 /**
708 * Check for message expiry.
709 */
710@@ -90,6 +104,13 @@
711 recompute();
712 }
713
714+void ChatOverlay::Impl::receive(const LogMessage& note)
715+{
716+ log_messages.push_back(note);
717+ recompute();
718+}
719+
720+
721 /**
722 * Recompute the chat message display.
723 */
724@@ -99,18 +120,54 @@
725
726 havemessages = false;
727
728- const std::vector<ChatMessage> & msgs = chat->getMessages();
729- uint32_t idx = msgs.size();
730+ // Parse the chat message list as well as the log message list
731+ // and display them in chronological order
732+ int32_t chat_idx = chat != nullptr ? chat->getMessages().size() - 1 : -1;
733+ int32_t log_idx = log_messages.empty() ? -1 : log_messages.size() - 1;
734+ int32_t msg_time = now;
735 std::string richtext;
736- while (idx && now - msgs[idx - 1].time <= CHAT_DISPLAY_TIME) {
737- richtext = msgs[idx - 1].toPrintable() + richtext;
738+
739+ while ((chat_idx >= 0 || log_idx >= 0) && now - msg_time < CHAT_DISPLAY_TIME) {
740+ if
741+ (chat_idx < 0 ||
742+ (log_idx >= 0 && chat->getMessages()[chat_idx].time < log_messages[log_idx].time))
743+ {
744+ // Log message is more recent
745+ msg_time = log_messages[log_idx].time;
746+ // Do some richtext formatting here
747+ richtext = "<p><font face=DejaVuSerif size=14 color=dddddd bold=1>"
748+ + log_messages[log_idx].msg + "<br></font></p>" + richtext;
749+ log_idx--;
750+ } else if
751+ (log_idx < 0 ||
752+ (chat_idx >= 0 && chat->getMessages()[chat_idx].time >= log_messages[log_idx].time))
753+ {
754+ // Chat message is more recent
755+ msg_time = chat->getMessages()[chat_idx].time;
756+ richtext = chat->getMessages()[chat_idx].toPrintable()
757+ + richtext;
758+ chat_idx--;
759+ } else {
760+ // Shoudn't happen
761+ assert(false);
762+ }
763 havemessages = true;
764- oldest = msgs[idx - 1].time;
765- idx--;
766- }
767-
768- if (havemessages)
769- rt.parse("<rt>" + richtext + "</rt>");
770+ oldest = msg_time;
771+ }
772+
773+ // Parse log messages to clear old ones
774+ while (!log_messages.empty()) {
775+ msg_time = log_messages.front().time;
776+ if (msg_time < now - CHAT_DISPLAY_TIME) {
777+ log_messages.erase(log_messages.begin());
778+ } else {
779+ break;
780+ }
781+ }
782+
783+ if (havemessages) {
784+ all_text = "<rt>" + richtext + "</rt>";
785+ }
786 }
787
788 void ChatOverlay::draw(RenderTarget & dst)
789@@ -118,8 +175,19 @@
790 if (!m->havemessages)
791 return;
792
793- int32_t height = m->rt.height();
794+ const Image* im = UI::g_fh1->render(m->all_text, get_w());
795+ // Background
796+ int32_t height = im->height() > get_h() ? get_h() : im->height();
797 int32_t top = get_h() - height - 2 * MARGIN;
798
799- m->rt.draw(dst, Point(MARGIN, top + MARGIN), !m->transparent);
800+ //FIXME: alpha channel not respected
801+ if (!m->transparent) {
802+ Rect rect(0, top, im->width(), height);
803+ dst.fill_rect(rect, RGBAColor(50, 50, 50, 128));
804+ }
805+ int32_t topcrop = im->height() - height;
806+ Rect cropRect(0, topcrop, im->width(), height);
807+
808+ Point pt(0, top);
809+ dst.blitrect(pt, im, cropRect);
810 }
811
812=== modified file 'src/wui/chatoverlay.h'
813--- src/wui/chatoverlay.h 2013-07-21 08:07:18 +0000
814+++ src/wui/chatoverlay.h 2013-08-21 08:32:15 +0000
815@@ -20,9 +20,11 @@
816 #ifndef CHATOVERLAY_H
817 #define CHATOVERLAY_H
818
819+#include "logic/notification.h"
820 #include "ui_basic/panel.h"
821
822 struct ChatProvider;
823+struct LogMessage;
824
825 /**
826 * The overlay that displays all new chat messages for some timeout on the main window.
827@@ -34,6 +36,7 @@
828 ~ChatOverlay();
829
830 void setChatProvider(ChatProvider &);
831+ void setLogProvider(Widelands::NoteSender<LogMessage> &);
832 virtual void draw(RenderTarget &);
833 virtual void think();
834
835
836=== modified file 'src/wui/game_main_menu_save_game.cc'
837--- src/wui/game_main_menu_save_game.cc 2013-08-01 10:51:41 +0000
838+++ src/wui/game_main_menu_save_game.cc 2013-08-21 08:32:15 +0000
839@@ -35,7 +35,6 @@
840 #include "profile/profile.h"
841 #include "timestring.h"
842 #include "wui/interactive_gamebase.h"
843-#include "wui/interactive_player.h"
844
845 using boost::format;
846
847@@ -144,11 +143,7 @@
848 }
849
850 m_editbox->focus();
851- if (parent.game().get_ipl() && !parent.game().get_ipl()->is_multiplayer()) {
852- // Pause the game only if we are part of the game
853- // and not in multiplayer
854- parent.game().gameController()->setPaused(true);
855- }
856+ pause_game(true);
857 }
858
859
860@@ -320,10 +315,8 @@
861
862 void Game_Main_Menu_Save_Game::die()
863 {
864+ pause_game(false);
865 UI::UniqueWindow::die();
866- if (igbase().game().get_ipl() && !igbase().game().get_ipl()->is_multiplayer()) {
867- igbase().game().gameController()->setPaused(false);
868- }
869 }
870
871
872@@ -372,3 +365,12 @@
873 if (g_fs->FileExists(complete_filename))
874 new DeletionMessageBox(*this, complete_filename);
875 }
876+
877+void Game_Main_Menu_Save_Game::pause_game(bool paused)
878+{
879+ if (igbase().is_multiplayer()) {
880+ return;
881+ }
882+ igbase().game().gameController()->setPaused(paused);
883+}
884+
885
886=== modified file 'src/wui/game_main_menu_save_game.h'
887--- src/wui/game_main_menu_save_game.h 2013-07-26 19:16:51 +0000
888+++ src/wui/game_main_menu_save_game.h 2013-08-21 08:32:15 +0000
889@@ -39,9 +39,10 @@
890
891 void fill_list();
892 void select_by_name(std::string name);
893+protected:
894+ virtual void die();
895 private:
896 Interactive_GameBase & igbase();
897- void die();
898 void selected (uint32_t);
899 void double_clicked(uint32_t);
900 void edit_box_changed();
901@@ -49,6 +50,7 @@
902 void delete_clicked();
903
904 bool save_game(std::string);
905+ void pause_game(bool paused);
906
907 UI::Listselect<std::string> m_ls;
908 UI::EditBox * m_editbox;
909
910=== modified file 'src/wui/gamechatpanel.cc'
911--- src/wui/gamechatpanel.cc 2013-07-26 20:19:36 +0000
912+++ src/wui/gamechatpanel.cc 2013-08-21 08:32:15 +0000
913@@ -52,7 +52,8 @@
914
915 std::string str = "<rt>";
916 for (uint32_t i = 0; i < msgs.size(); ++i) {
917- str += msgs[i].toPrintable();
918+ // FIXME use toPrintable() when old renderer is kicked out
919+ str += msgs[i].toOldRichText();
920 str += '\n';
921 }
922 str += "</rt>";
923
924=== modified file 'src/wui/interactive_base.cc'
925--- src/wui/interactive_base.cc 2013-07-26 20:19:36 +0000
926+++ src/wui/interactive_base.cc 2013-08-21 08:32:15 +0000
927@@ -93,6 +93,8 @@
928 m_road_buildhelp_overlay_jobid(Overlay_Manager::Job_Id::Null()),
929 m_buildroad (0),
930 m_road_build_player (0),
931+ // Initialize chatoveraly before the toolbar so it is below
932+ m_chatOverlay (new ChatOverlay(this, 10, 25, get_w() / 2, get_h() - 25)),
933 m_toolbar (this, 0, 0, UI::Box::Horizontal),
934 m_label_speed_shadow
935 (this, get_w() - 1, 0, std::string(), UI::Align_TopRight),
936@@ -115,6 +117,8 @@
937 set_dock_windows_to_edges
938 (global_s.get_bool("dock_windows_to_edges", false));
939
940+ m_chatOverlay->setLogProvider(m_log_sender);
941+
942 // Switch to the new graphics system now, if necessary.
943 WLApplication::get()->refresh_graphics();
944
945@@ -761,6 +765,16 @@
946 return m_buildroad->get_end();
947 }
948
949+void Interactive_Base::log_message(const std::string& message) const
950+{
951+ // Send to linked receivers
952+ LogMessage lm;
953+ lm.msg = message;
954+ lm.time = time(nullptr);
955+ m_log_sender.send(lm);
956+}
957+
958+
959
960 /*
961 ===============
962
963=== modified file 'src/wui/interactive_base.h'
964--- src/wui/interactive_base.h 2013-07-26 20:19:36 +0000
965+++ src/wui/interactive_base.h 2013-08-21 08:32:15 +0000
966@@ -25,6 +25,9 @@
967 #include "debugconsole.h"
968 #include "logic/editor_game_base.h"
969 #include "logic/map.h"
970+#include "logic/notification.h"
971+#include "logmessage.h"
972+#include "wui/chatoverlay.h"
973 #include "wui/mapview.h"
974 #include "wui/overlay_manager.h"
975 #include "ui_basic/box.h"
976@@ -111,6 +114,13 @@
977
978 virtual void cleanup_for_load() {};
979
980+ /**
981+ * Log a message to be displayed on screen
982+ */
983+ void log_message(const std::string& message) const;
984+ void log_message(const char* message) const {
985+ log_message(std::string(message));
986+ }
987 private:
988 void roadb_add_overlay ();
989 void roadb_remove_overlay();
990@@ -169,6 +179,7 @@
991 m_toolbar.set_pos
992 (Point((get_inner_w() - m_toolbar.get_w()) >> 1, get_inner_h() - 34));
993 }
994+ ChatOverlay * m_chatOverlay;
995 UI::Box m_toolbar;
996
997
998@@ -181,6 +192,7 @@
999 UI::Textarea m_label_speed;
1000
1001 UI::UniqueWindow::Registry m_debugconsole;
1002+ Widelands::NoteSender<LogMessage> m_log_sender;
1003 };
1004
1005 #define PIC2 g_gr->images().get("pics/but2.png")
1006
1007=== modified file 'src/wui/interactive_gamebase.cc'
1008--- src/wui/interactive_gamebase.cc 2013-08-01 10:51:41 +0000
1009+++ src/wui/interactive_gamebase.cc 2013-08-21 08:32:15 +0000
1010@@ -25,16 +25,14 @@
1011 #include "logic/ship.h"
1012 #include "profile/profile.h"
1013 #include "upcast.h"
1014-#include "wui/chatoverlay.h"
1015 #include "wui/game_summary.h"
1016
1017 Interactive_GameBase::Interactive_GameBase
1018 (Widelands::Game & _game, Section & global_s,
1019- PlayerType pt, bool const chatenabled)
1020+ PlayerType pt, bool const chatenabled, bool const multiplayer)
1021 :
1022 Interactive_Base(_game, global_s),
1023 m_chatProvider(0),
1024- m_chatOverlay(0),
1025 m_building_census_format
1026 (global_s.get_string("building_census_format", "%N")),
1027 m_building_statistics_format
1028@@ -42,7 +40,8 @@
1029 m_building_tooltip_format
1030 (global_s.get_string("building_tooltip_format", "%r")),
1031 m_chatenabled(chatenabled),
1032- m_playertype(pt)
1033+ m_playertype(pt),
1034+ m_multiplayer(multiplayer)
1035 {}
1036
1037 /// \return a pointer to the running \ref Game instance.
1038
1039=== modified file 'src/wui/interactive_gamebase.h'
1040--- src/wui/interactive_gamebase.h 2013-08-01 10:51:41 +0000
1041+++ src/wui/interactive_gamebase.h 2013-08-21 08:32:15 +0000
1042@@ -24,7 +24,6 @@
1043 #include "wui/interactive_base.h"
1044 #include "logic/game.h"
1045
1046-struct ChatOverlay;
1047 struct ChatProvider;
1048
1049 enum PlayerType {NONE, OBSERVER, PLAYING, VICTORIOUS, DEFEATED};
1050@@ -51,7 +50,8 @@
1051 (Widelands::Game &,
1052 Section & global_s,
1053 PlayerType pt = NONE,
1054- bool chatenabled = false);
1055+ bool chatenabled = false,
1056+ bool multiplayer = false);
1057 Widelands::Game * get_game() const;
1058 Widelands::Game & game() const;
1059
1060@@ -78,17 +78,18 @@
1061 void set_playertype(const PlayerType & pt) {m_playertype = pt;}
1062
1063 bool try_show_ship_window();
1064+ bool is_multiplayer() {return m_multiplayer;}
1065
1066 void show_game_summary();
1067
1068 protected:
1069 Game_Main_Menu_Windows m_mainm_windows;
1070 ChatProvider * m_chatProvider;
1071- ChatOverlay * m_chatOverlay;
1072 std::string m_building_census_format;
1073 std::string m_building_statistics_format;
1074 std::string m_building_tooltip_format;
1075 bool m_chatenabled;
1076+ bool m_multiplayer;
1077
1078 PlayerType m_playertype;
1079 UI::UniqueWindow::Registry m_fieldaction;
1080
1081=== modified file 'src/wui/interactive_player.cc'
1082--- src/wui/interactive_player.cc 2013-07-26 20:19:36 +0000
1083+++ src/wui/interactive_player.cc 2013-08-21 08:32:15 +0000
1084@@ -46,7 +46,6 @@
1085 #include "ui_basic/unique_window.h"
1086 #include "upcast.h"
1087 #include "wui/building_statistics_menu.h"
1088-#include "wui/chatoverlay.h"
1089 #include "wui/encyclopedia_window.h"
1090 #include "wui/fieldaction.h"
1091 #include "wui/game_chat_menu.h"
1092@@ -83,10 +82,9 @@
1093 bool const scenario,
1094 bool const multiplayer)
1095 :
1096- Interactive_GameBase (_game, global_s),
1097+ Interactive_GameBase (_game, global_s, NONE, multiplayer, multiplayer),
1098 m_auto_roadbuild_mode(global_s.get_bool("auto_roadbuild_mode", true)),
1099 m_flag_to_connect(Widelands::Coords::Null()),
1100- m_multiplayer(multiplayer),
1101
1102 // Chat is different, as m_chatProvider needs to be checked when toggling
1103 // Buildhelp is different as it does not toggle a UniqueWindow
1104@@ -153,10 +151,6 @@
1105 m_toolbar.add(&m_toggle_statistics_menu, UI::Box::AlignLeft);
1106 m_toolbar.add(&m_toggle_minimap, UI::Box::AlignLeft);
1107 m_toolbar.add(&m_toggle_buildhelp, UI::Box::AlignLeft);
1108- // Limit chat width to half the screen, to limit the damage lamers can do
1109- // by flooding chat messages
1110- m_chatOverlay =
1111- new ChatOverlay(this, 10, 25, get_w() / 2, get_h() - 25);
1112 if (multiplayer) {
1113 m_toolbar.add(&m_toggle_chat, UI::Box::AlignLeft);
1114 m_toggle_chat.set_visible(false);
1115@@ -284,7 +278,7 @@
1116 m_flag_to_connect = Widelands::Coords::Null();
1117 }
1118 }
1119- if (m_multiplayer) {
1120+ if (is_multiplayer()) {
1121 m_toggle_chat.set_visible(m_chatenabled);
1122 m_toggle_chat.set_enabled(m_chatenabled);
1123 }
1124@@ -455,7 +449,7 @@
1125
1126 case SDLK_KP_ENTER:
1127 case SDLK_RETURN:
1128- if (!m_chatProvider | !m_chatenabled || !m_multiplayer)
1129+ if (!m_chatProvider | !m_chatenabled || !is_multiplayer())
1130 break;
1131
1132 if (!m_chat.window)
1133
1134=== modified file 'src/wui/interactive_player.h'
1135--- src/wui/interactive_player.h 2013-07-26 20:19:36 +0000
1136+++ src/wui/interactive_player.h 2013-08-21 08:32:15 +0000
1137@@ -86,15 +86,12 @@
1138
1139 void popup_message(Widelands::Message_Id, const Widelands::Message &);
1140
1141- bool is_multiplayer() {return m_multiplayer;}
1142-
1143 private:
1144 void cmdSwitchPlayer(const std::vector<std::string> & args);
1145
1146 Widelands::Player_Number m_player_number;
1147 bool m_auto_roadbuild_mode;
1148 Widelands::Coords m_flag_to_connect;
1149- bool m_multiplayer;
1150
1151 UI::Button m_toggle_chat;
1152 UI::Button m_toggle_options_menu;
1153
1154=== modified file 'src/wui/interactive_spectator.cc'
1155--- src/wui/interactive_spectator.cc 2013-07-26 20:19:36 +0000
1156+++ src/wui/interactive_spectator.cc 2013-08-21 08:32:15 +0000
1157@@ -28,7 +28,6 @@
1158 #include "ui_basic/textarea.h"
1159 #include "ui_basic/unique_window.h"
1160 #include "upcast.h"
1161-#include "wui/chatoverlay.h"
1162 #include "wui/fieldaction.h"
1163 #include "wui/game_chat_menu.h"
1164 #include "wui/game_main_menu_save_game.h"
1165@@ -41,7 +40,7 @@
1166 Interactive_Spectator::Interactive_Spectator
1167 (Widelands::Game & _game, Section & global_s, bool const multiplayer)
1168 :
1169- Interactive_GameBase(_game, global_s, OBSERVER, multiplayer),
1170+ Interactive_GameBase(_game, global_s, OBSERVER, multiplayer, multiplayer),
1171
1172 #define INIT_BTN(picture, name, tooltip) \
1173 TOOLBAR_BUTTON_COMMON_PARAMETERS(name), \
1174@@ -69,7 +68,7 @@
1175 m_toggle_minimap.sigclicked.connect(boost::bind(&Interactive_Spectator::toggle_minimap, this));
1176
1177 m_toolbar.set_layout_toplevel(true);
1178- if (!multiplayer) {
1179+ if (!is_multiplayer()) {
1180 m_toolbar.add(&m_exit, UI::Box::AlignLeft);
1181 m_toolbar.add(&m_save, UI::Box::AlignLeft);
1182 } else
1183@@ -80,9 +79,7 @@
1184
1185 // TODO : instead of making unneeded buttons invisible after generation,
1186 // they should not at all be generated. -> implement more dynamic toolbar UI
1187- if (multiplayer) {
1188- m_chatOverlay =
1189- new ChatOverlay(this, 10, 25, get_w() - 10, get_h() - 25);
1190+ if (is_multiplayer()) {
1191 m_exit.set_visible(false);
1192 m_exit.set_enabled(false);
1193 m_save.set_visible(false);
1194@@ -172,28 +169,30 @@
1195
1196 void Interactive_Spectator::exit_btn()
1197 {
1198- if (m_chatenabled) // == multiplayer
1199+ if (is_multiplayer()) {
1200 return;
1201+ }
1202 end_modal(0);
1203 }
1204
1205
1206 void Interactive_Spectator::save_btn()
1207 {
1208- if (m_chatenabled) // == multiplayer
1209+ if (is_multiplayer()) {
1210 return;
1211+ }
1212 if (m_mainm_windows.savegame.window)
1213 delete m_mainm_windows.savegame.window;
1214 else {
1215- game().gameController()->setDesiredSpeed(0);
1216 new Game_Main_Menu_Save_Game(*this, m_mainm_windows.savegame);
1217 }
1218 }
1219
1220
1221 void Interactive_Spectator::toggle_options_menu() {
1222- if (!m_chatenabled) // == !multiplayer
1223+ if (!is_multiplayer()) {
1224 return;
1225+ }
1226 if (m_options.window)
1227 delete m_options.window;
1228 else
1229@@ -259,7 +258,6 @@
1230
1231 case SDLK_s:
1232 if (code.mod & (KMOD_LCTRL | KMOD_RCTRL)) {
1233- game().gameController()->setDesiredSpeed(0);
1234 new Game_Main_Menu_Save_Game(*this, m_mainm_windows.savegame);
1235 } else
1236 set_display_flag
1237
1238=== modified file 'src/wui/watchwindow.cc'
1239--- src/wui/watchwindow.cc 2013-08-02 18:36:03 +0000
1240+++ src/wui/watchwindow.cc 2013-08-21 08:32:15 +0000
1241@@ -256,12 +256,14 @@
1242 pos = bob->calc_drawpos(game(), pos);
1243
1244 Widelands::Map & map = game().map();
1245- if (1 < game().get_ipl()->player().vision(map.get_index(bob->get_position(), map.get_width()))) {
1246+ // Drop the tracking if it leaves our vision range
1247+ Interactive_Player* ipl = game().get_ipl();
1248+ if (ipl && 1 >= ipl->player().vision(map.get_index(bob->get_position(), map.get_width()))) {
1249+ // Not in sight
1250+ views[cur_index].tracking = 0;
1251+ } else {
1252 mapview.set_viewpoint
1253 (pos - Point(mapview.get_w() / 2, mapview.get_h() / 2), false);
1254- } else {
1255- // stop tracking
1256- views[cur_index].tracking = 0;
1257 }
1258 }
1259
1260@@ -327,9 +329,10 @@
1261 p = bob->calc_drawpos(g, p);
1262 int32_t const dist =
1263 MapviewPixelFunctions::calc_pix_distance(map, p, pos);
1264+ Interactive_Player* ipl = game().get_ipl();
1265 if
1266 ((!closest || closest_dist > dist)
1267- && (1 < game().get_ipl()->player().vision(map.get_index(bob->get_position(), map.get_width()))))
1268+ && (!ipl || 1 < ipl->player().vision(map.get_index(bob->get_position(), map.get_width()))))
1269 {
1270 closest = bob;
1271 closest_dist = dist;

Subscribers

People subscribed via source and target branches

to status/vote changes: