The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
about.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2016 by David White <[email protected]>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Show screen with scrolling credits.
18  */
19 
20 #include "about.hpp"
21 #include "global.hpp" // for false_, bool_
22 
23 #include "config.hpp" // for config, etc
24 #include "cursor.hpp" // for setter, CURSOR_TYPE::WAIT
25 #include "events.hpp" // for pump, raise_draw_event, etc
26 #include "font.hpp" // for NORMAL_COLOR, SIZE_XLARGE
27 #include "game_config.hpp" // for game_title_background
28 #include "gettext.hpp" // for _
29 #include "image.hpp" // for get_image
30 #include "key.hpp" // for CKey
31 #include "marked-up_text.hpp" // for draw_text, LARGE_TEXT, etc
32 #include "sdl/rect.hpp" // for create_rect
33 #include "sdl/utils.hpp" // for surface, sdl_blit, etc
34 #include "serialization/string_utils.hpp" // for split, etc
35 #include "show_dialog.hpp" // for dialog_frame, etc
36 #include "tstring.hpp" // for operator==
37 #include "video.hpp" // for update_rect, CVideo
38 #include "widgets/button.hpp" // for button
39 
40 #include <algorithm> // for max
41 #include <boost/scoped_ptr.hpp> // for scoped_ptr
42 #include <map> // for map, map<>::mapped_type
43 #include <ostream> // for operator<<, basic_ostream, etc
44 
45 #include "sdl/alpha.hpp"
46 
47 /**
48  * @namespace about
49  * Display credits %about all contributors.
50  *
51  * This module is used from the startup screen. \n
52  * When show_about() is called, a list of contributors
53  * to the game will be presented to the user.
54  */
55 namespace about
56 {
57  static config about_list = config();
58  static std::map<std::string , std::string> images;
60 
61 /**
62  * Given a vector of strings, and a config representing an [about] section,
63  * add all the credits lines from the about section to the list of strings.
64  */
65 static void add_lines(std::vector<std::string> &res, config const &c, bool split_multiline_headers) {
66  std::string title = c["title"];
67  if (!title.empty()) {
68  if(split_multiline_headers) {
69  // If the title is multi-line, we need to split it accordingly or we
70  // get slight scrolling glitches in the credits screen.
71  const std::vector<std::string>& lines = utils::split(c["title"], '\n');
72  bool first = true;
73  for(const std::string& line : lines) {
74  if(first) {
75  res.push_back("+" + line);
76  first = false;
77  } else {
78  // Don't convert other lines into headers or they get extra
79  // spacing on the credits screen.
80  res.push_back(line);
81  }
82  }
83  } else {
84  res.push_back("+" + title);
85  }
86  }
87 
88  std::vector<std::string> lines = utils::split(c["text"], '\n');
89  for(std::string &line : lines)
90  {
91  if (line.size() > 1 && line[0] == '+')
92  line = "+ " + line.substr(1);
93  else
94  line = "- " + line;
95 
96  if (!line.empty())
97  {
98  if (line[0] == '_')
99  line = translation::gettext(line.substr(1).c_str());
100  res.push_back(line);
101  }
102  }
103 
104  for (const config &entry : c.child_range("entry")) {
105  res.push_back("- "+ entry["name"].str());
106  }
107 }
108 
109 
110 std::vector<std::string> get_text(const std::string &campaign, bool split_multiline_headers)
111 {
112  std::vector< std::string > res;
113 
114  config::child_itors about_entries = about_list.child_range("about");
115 
116  if (!campaign.empty()) {
117  for (const config &about : about_entries) {
118  // just finished a particular campaign
119  if (campaign == about["id"]) {
120  add_lines(res, about, split_multiline_headers);
121  }
122  }
123  }
124 
125  for (const config &about : about_entries) {
126  add_lines(res, about, split_multiline_headers);
127  }
128 
129  return res;
130 }
131 
132 void set_about(const config &cfg)
133 {
134  about_list.clear();
135  images.clear();
136  images_default = "";
137 
138  for (const config &about : cfg.child_range("about"))
139  {
140  about_list.add_child("about", about);
141  const std::string &im = about["images"];
142  if (!images.empty())
143  {
144  if (images_default.empty())
145  images_default = im;
146  else
147  images_default += ',' + im;
148  }
149  }
150 
151  for (const config &campaign : cfg.child_range("campaign"))
152  {
153  config::const_child_itors abouts = campaign.child_range("about");
154  if (abouts.first == abouts.second) continue;
155 
156  config temp;
157  std::ostringstream text;
158  const std::string &id = campaign["id"];
159  temp["title"] = campaign["name"];
160  temp["id"] = id;
161  std::string campaign_images;
162 
163  for (const config &about : abouts)
164  {
165  const std::string &subtitle = about["title"];
166  if (!subtitle.empty())
167  {
168  text << '+';
169  if (subtitle[0] == '_')
170  text << translation::gettext(subtitle.substr(1, subtitle.size() - 1).c_str());
171  else
172  text << subtitle;
173  text << '\n';
174  }
175 
176  for (const std::string &line : utils::split(about["text"], '\n'))
177  {
178  text << " " << line << '\n';
179  }
180 
181  for (const config &entry : about.child_range("entry"))
182  {
183  text << " " << entry["name"] << '\n';
184  }
185 
186  const std::string &im = about["images"];
187  if (!im.empty())
188  {
189  if (campaign_images.empty())
190  campaign_images = im;
191  else
192  campaign_images += ',' + im;
193  }
194  }
195 
196  images[id] = campaign_images;
197  temp["text"] = text.str();
198  about_list.add_child("about",temp);
199  }
200 }
201 
202 /**
203  * Show credits with list of contributors.
204  *
205  * Names of people are shown scrolling up like in movie-credits.\n
206  * Uses map from wesnoth or campaign as background.
207  */
208 void show_about(CVideo &video, const std::string &campaign)
209 {
210  boost::scoped_ptr<cursor::setter> cur(new cursor::setter(cursor::WAIT));
211  surface& screen = video.getSurface();
212  if (screen == nullptr) return;
213 
214  // If the title is multi-line, we need to split it accordingly or we
215  // get slight scrolling glitches in the credits screen.
216  std::vector<std::string> text = about::get_text(campaign, true);
217 
218  SDL_Rect screen_rect = sdl::create_rect(0, 0, screen->w, screen->h);
219 
220  const surface_restorer restorer(&video, screen_rect);
221 
222  cur.reset();
223 
224  std::vector<std::string> image_list;
225  if(campaign.size() && !images[campaign].empty()){
226  image_list = utils::parenthetical_split(images[campaign], ',');
227  } else{
228  image_list = utils::parenthetical_split(images_default, ',');
229  }
230 
231  surface map_image, map_image_scaled;
232 
233  if(!image_list.empty()) {
234  map_image = image::get_image(image_list[0]);
235  } else {
236  image_list.push_back("");
237  }
238 
239  if(!map_image){
241  map_image=image::get_image(image_list[0]);
242  }
243 
244  gui::button close(video,_("Close"));
245  close.set_location((screen->w/2)-(close.width()/2), screen->h - 30);
246 
247  const int def_size = font::SIZE_XLARGE;
248  const SDL_Color def_color = font::NORMAL_COLOR;
249 
250  //substitute in the correct control characters for '+' and '-'
251  std::string before_header(2, ' ');
252  before_header[0] = font::LARGE_TEXT;
253  for(unsigned i = 0; i < text.size(); ++i) {
254  std::string &s = text[i];
255  if (s.empty()) continue;
256  char &first = s[0];
257  if (first == '-')
258  first = font::SMALL_TEXT;
259  else if (first == '+') {
260  first = font::LARGE_TEXT;
261  text.insert(text.begin() + i, before_header);
262  ++i;
263  }
264  }
265  text.insert(text.begin(), 10, before_header);
266 
267  int startline = 0;
268 
269  //TODO: use values proportional to screen ?
270  // distance from top of map image to top of scrolling text
271  const int top_margin = 60;
272  // distance from bottom of scrolling text to bottom of map image
273  const int bottom_margin = 40;
274  // distance from left of scrolling text to the frame border
275  const int text_left_padding = screen->w/32;
276 
277  int offset = 0;
278  bool is_new_line = true;
279 
280  int first_line_height = 0;
281 
282  SDL_Rect frame_area;
283 
284  // we use a dialog to contains the text. Strange idea but at least the style
285  // will be consistent with the titlescreen
287 
288  // the text area's dimensions
289  SDL_Rect text_rect = { 0, 0, 0, 0 };
290  // we'll retain a copy to prevent SDL_blit to change its w and h
291  SDL_Rect text_rect_blit;
292 
293  surface text_surf;
294 
295  CKey key;
296  bool last_escape;
297 
298  int image_count = 0;
299  int scroll_speed = 4; // scroll_speed*50 = speed of scroll in pixel per second
300 
301  // Initially redraw all
302  bool redraw_mapimage = true;
303  bool update_dimensions = true;
304  int max_text_width = 0;
305 
306  do {
307  last_escape = key[SDLK_ESCAPE] != 0;
308 
309  // check to see if background image has changed
310  if(text.size() && (image_count <
311  ((startline * static_cast<int>(image_list.size())) /
312  static_cast<int>(text.size())))){
313 
314  image_count++;
315  surface temp=image::get_image(image_list[image_count]);
316  map_image=temp?temp:map_image;
317  redraw_mapimage = true;
318  }
319 
320  if (update_dimensions) {
321  // rescale the background
322  map_image_scaled = scale_surface(map_image, screen->w, screen->h);
323  screen_rect = sdl::create_rect(0, 0, screen->w, screen->h);
324  redraw_mapimage = true;
325 
326  // update the frame
327  frame_area = sdl::create_rect(
328  screen->w * 3 / 32
329  , top_margin
330  , screen->w * 13 / 16
331  , screen->h - top_margin - bottom_margin);
332 
333  text_rect = f.layout(frame_area).interior;
334 
335  // update the text area
336  text_rect.x += text_left_padding;
337  text_rect.w -= text_left_padding;
338  text_rect_blit = text_rect;
339 
340  text_surf = create_compatible_surface(screen, text_rect.w, text_rect.h);
341  SDL_SetAlpha(text_surf, SDL_RLEACCEL, SDL_ALPHA_OPAQUE);
342 
343  // relocate the close button
344  close.set_location((screen->w/2)-(close.width()/2), screen->h - 30);
345 
346  update_dimensions = false;
347  }
348 
349  if (redraw_mapimage) {
350  // draw map to screen, thus erasing all text
351  sdl_blit(map_image_scaled, nullptr, screen, nullptr);
352  update_rect(screen_rect);
353 
354  // redraw the dialog
355  f.draw_background();
356  f.draw_border();
357  // cache the dialog background (alpha blending + blurred map)
358  sdl_blit(screen, &text_rect, text_surf, nullptr);
359  redraw_mapimage = false;
360  } else {
361  // redraw the saved part of the dialog where text scrolled
362  // thus erasing all text
363  SDL_Rect modified = sdl::create_rect(0, 0, max_text_width, text_rect.h);
364  sdl_blit(text_surf, &modified, screen, &text_rect_blit);
365  update_rect(text_rect);
366  }
367 
368  int y = text_rect.y - offset;
369  int line = startline;
370  max_text_width = 0;
371 
372  {
373  // clip to keep text into the frame (thus the new code block)
374  clip_rect_setter set_clip_rect(screen, &text_rect);
375 
376  const int line_spacing = 5;
377  do {
378  // draw the text (with ellipsis if needed)
379  // update the max_text_width for future cleaning
380  int w = font::draw_text(&video, text_rect, def_size, def_color,
381  text[line], text_rect.x, y).w;
382  max_text_width = std::max<int>(max_text_width, w);
383  // since the real drawing on screen is clipped,
384  // we do a dummy one to get the height of the not clipped line.
385  // (each time because special format characters may change it)
386  const int line_height = font::draw_text(nullptr, text_rect, def_size, def_color,
387  text[line], 0,0).h;
388 
389  if(is_new_line) {
390  is_new_line = false;
391  first_line_height = line_height + line_spacing;
392  }
393  line++;
394  if(size_t(line) > text.size()-1)
395  line = 0;
396  y += line_height + line_spacing;
397  } while(y < text_rect.y + text_rect.h);
398  }
399 
400  // performs the actual scrolling
401  offset += scroll_speed;
402  if (offset>=first_line_height) {
403  offset -= first_line_height;
404  is_new_line = true;
405  startline++;
406  if(size_t(startline) == text.size()){
407  startline = 0;
408  image_count = -1;
409  }
410  }
411 
412  // handle events
413  if (key[SDLK_UP] && scroll_speed < 20) {
414  ++scroll_speed;
415  }
416  if (key[SDLK_DOWN] && scroll_speed > 0) {
417  --scroll_speed;
418  }
419  if (screen->w != screen_rect.w || screen->h != screen_rect.h) {
420  update_dimensions = true;
421  }
422 
423  events::pump();
426 
427  // flip screen and wait, so the text does not scroll too fast
428  video.flip();
429  CVideo::delay(20);
430 
431  } while(!close.pressed() && (last_escape || !key[SDLK_ESCAPE]));
432 }
433 
434 } // end namespace about
void show_about(CVideo &video, const std::string &campaign)
Show credits with list of contributors.
Definition: about.cpp:208
surface get_image(const image::locator &i_locator, TYPE type)
function to get the surface corresponding to an image.
Definition: image.cpp:878
child_itors child_range(const std::string &key)
Definition: config.cpp:613
static void add_lines(std::vector< std::string > &res, config const &c, bool split_multiline_headers)
Given a vector of strings, and a config representing an [about] section, add all the credits lines fr...
Definition: about.cpp:65
const GLfloat * c
Definition: glew.h:12741
Definition: video.hpp:58
game_display * screen
Definition: resources.cpp:27
void flip()
Definition: video.cpp:496
std::vector< std::string > get_text(const std::string &campaign, bool split_multiline_headers)
Definition: about.cpp:110
int scroll_speed()
const char LARGE_TEXT
Standard markups for color, size, font, images.
GLint GLint GLint GLint GLint GLint y
Definition: glew.h:1220
void clear()
Definition: config.cpp:1055
surface scale_surface(const surface &surf, int w, int h)
Definition: utils.cpp:443
const SDL_Color NORMAL_COLOR
Definition: font.cpp:564
-file util.hpp
Definitions for the interface to Wesnoth Markup Language (WML).
surface & getSurface()
Definition: dummy_video.cpp:22
std::pair< const_child_iterator, const_child_iterator > const_child_itors
Definition: config.hpp:214
Display credits about all contributors.
const int SIZE_XLARGE
Definition: font.hpp:68
GLintptr offset
Definition: glew.h:1650
dimension_measurements layout(int x, int y, int w, int h)
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:82
GLuint id
Definition: glew.h:1647
static std::string images_default
Definition: about.cpp:59
GLubyte GLubyte GLubyte GLubyte w
Definition: glew.h:1858
std::string game_title_background
Definition: game_config.cpp:84
config & add_child(const std::string &key)
Definition: config.cpp:743
bool pressed()
Definition: button.cpp:770
static UNUSEDNOWARN std::string gettext(const char *str)
Definition: gettext.hpp:64
void raise_draw_event()
Definition: events.cpp:565
void pump()
Definition: events.cpp:336
SDL_Rect draw_text(surface &dst, const SDL_Rect &area, int size, const SDL_Color &color, const std::string &txt, int x, int y, bool use_tooltips, int style)
Function to draw text on a surface.
GLuint res
Definition: glew.h:9258
void raise_process_event()
Definition: events.cpp:539
static const style titlescreen_style
Definition: show_dialog.hpp:72
std::pair< child_iterator, child_iterator > child_itors
Definition: config.hpp:213
size_t i
Definition: function.cpp:1057
void set_about(const config &cfg)
Definition: about.cpp:132
SDL_Rect create_rect(const int x, const int y, const int w, const int h)
Creates an empty SDL_Rect.
Definition: rect.cpp:28
std::vector< std::string > parenthetical_split(std::string const &val, const char separator, std::string const &left, std::string const &right, const int flags)
Splits a string based either on a separator where text within parenthesis is protected from splitting...
static std::map< std::string, std::string > images
Definition: about.cpp:58
static config about_list
Definition: about.cpp:57
Contains the SDL_Rect helper code.
const char SMALL_TEXT
surface create_compatible_surface(const surface &surf, int width, int height)
Definition: utils.cpp:2166
GLint * first
Definition: glew.h:1496
static void delay(unsigned int milliseconds)
Definition: video.cpp:490
virtual void set_location(SDL_Rect const &rect)
Definition: widget.cpp:85
std::vector< std::string > split(std::string const &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
int width() const
Definition: widget.cpp:134
void sdl_blit(const surface &src, SDL_Rect *src_rect, surface &dst, SDL_Rect *dst_rect)
Definition: utils.hpp:112
int SDL_SetAlpha(SDL_Surface *surface, Uint32 flag, Uint8 alpha)
Definition: alpha.cpp:18
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
Class that keeps track of all the keys on the keyboard.
Definition: key.hpp:27
Compatibility layer for using SDL 1.2 and 2.0.
GLdouble s
Definition: glew.h:1358
void update_rect(const SDL_Rect &)
Definition: dummy_video.cpp:27
GLsizei const GLcharARB ** string
Definition: glew.h:4503
GLclampf f
Definition: glew.h:3024