The Battle for Wesnoth  1.19.5+dev
game_config.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2024
3  by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #include "game_config.hpp"
17 
18 #include "color_range.hpp"
19 #include "config.hpp"
20 #include "gettext.hpp"
21 #include "log.hpp"
22 #include "game_version.hpp"
24 
25 #include <cmath>
26 #include <random>
27 
28 static lg::log_domain log_engine("engine");
29 #define LOG_NG LOG_STREAM(info, log_engine)
30 #define ERR_NG LOG_STREAM(err, log_engine)
31 
32 namespace game_config
33 {
34 //
35 // Gameplay constants
36 //
37 int base_income = 2;
40 int recall_cost = 20;
43 
44 int poison_amount = 8;
46 
48 
49 //
50 // Terrain-related constants
51 //
52 unsigned int tile_size = 72;
53 
54 std::string default_terrain;
56 
57 std::vector<unsigned int> zoom_levels {36, 72, 144};
58 
59 //
60 // Display scale constants
61 //
62 double hp_bar_scaling = 0.666;
63 double xp_bar_scaling = 0.5;
64 
65 //
66 // Misc
67 //
68 unsigned lobby_network_timer = 100;
69 unsigned lobby_refresh = 4000;
70 
71 const std::size_t max_loop = 65536;
72 
73 std::vector<server_info> server_list;
74 
75 bool allow_insecure = false;
76 
77 //
78 // Gamestate flags
79 //
80 bool
81  debug_impl = false,
82  debug_lua = false,
83  strict_lua = false,
84  editor = false,
86  mp_debug = false,
87  exit_at_end = false,
88  no_delay = false,
90  no_addons = false;
91 
92 const bool& debug = debug_impl;
93 
94 void set_debug(bool new_debug) {
95  // TODO: remove severity static casts and fix issue #7894
96  if(debug_impl && !new_debug) {
97  // Turning debug mode off; decrease deprecation severity
99  if(lg::get_log_domain_severity("deprecation", severity)) {
100  int severityInt = static_cast<int>(severity);
101  lg::set_log_domain_severity("deprecation", static_cast<lg::severity>(severityInt - 2));
102  }
103  } else if(!debug_impl && new_debug) {
104  // Turning debug mode on; increase deprecation severity
106  if(lg::get_log_domain_severity("deprecation", severity)) {
107  int severityInt = static_cast<int>(severity);
108  lg::set_log_domain_severity("deprecation", static_cast<lg::severity>(severityInt + 2));
109  }
110  }
111  debug_impl = new_debug;
112 }
113 
114 //
115 // Orb display flags
116 //
124 
125 //
126 // Music constants
127 //
129 
130 std::vector<std::string> default_defeat_music;
131 std::vector<std::string> default_victory_music;
132 
133 //
134 // Color info
135 //
136 std::string flag_rgb, unit_rgb;
137 
138 std::vector<color_t> red_green_scale;
139 std::vector<color_t> red_green_scale_text;
140 
141 std::vector<color_t> blue_white_scale;
142 std::vector<color_t> blue_white_scale_text;
143 
144 std::map<std::string, color_range, std::less<>> team_rgb_range;
145 // Map [color_range]id to [color_range]name, or "" if no name
146 std::map<std::string, t_string, std::less<>> team_rgb_name;
147 
148 std::map<std::string, std::vector<color_t>, std::less<>> team_rgb_colors;
149 
150 std::vector<std::string> default_colors;
151 
152 namespace colors
153 {
154 std::string ally_orb_color;
155 std::string enemy_orb_color;
156 std::string moved_orb_color;
157 std::string partial_orb_color;
158 std::string unmoved_orb_color;
159 std::string default_color_list;
160 } // namespace colors
161 
162 //
163 // Image constants
164 //
165 std::vector<std::string> foot_speed_prefix;
166 
168 std::string foot_teleport_exit;
169 
170 namespace images {
171 
172 std::string
180  // orbs and hp/xp bar
184  // top bar icons
187  // flags
190  // hex overlay
198  // GUI elements
202  // TODO: de-hardcode this
203  selected_menu = "buttons/radiobox-pressed.png",
204  deselected_menu = "buttons/radiobox.png",
205  checked_menu = "buttons/checkbox-pressed.png",
206  unchecked_menu = "buttons/checkbox.png",
207  wml_menu = "buttons/WML-custom.png",
212  // notifications icon
213  app_icon = "images/icons/icon-game.png";
214 
215 } //images
216 
217 //
218 // Sound constants
219 //
220 namespace sounds {
221 
222 std::string
223  turn_bell = "bell.wav",
224  timer_bell = "timer.wav",
225  public_message = "chat-[1~3].ogg",
226  private_message = "chat-highlight.ogg",
227  friend_message = "chat-friend.ogg",
228  server_message = "receive.wav",
229  player_joins = "arrive.wav",
230  player_leaves = "leave.wav",
231  game_user_arrive = "join.wav",
232  game_user_leave = "leave.wav",
233  ready_for_start = "bell.wav",
234  game_has_begun = "gamestart.ogg",
235  game_created = "join.wav";
236 
237 const std::string
238  button_press = "button.wav",
239  checkbox_release = "checkbox.wav",
240  slider_adjust = "slider.wav",
241  menu_expand = "expand.wav",
242  menu_contract = "contract.wav",
243  menu_select = "select.wav";
244 
245 namespace status {
246 
247 std::string
248  poisoned = "poison.ogg",
249  slowed = "slowed.wav",
250  petrified = "petrified.ogg";
251 
252 } // status
253 
254 } // sounds
255 
256 static void add_color_info(const game_config_view& v, bool build_defaults);
258 {
259  add_color_info(v, false);
260 }
261 
262 void load_config(const config &v)
263 {
264  base_income = v["base_income"].to_int(2);
265  village_income = v["village_income"].to_int(1);
266  village_support = v["village_support"].to_int(1);
267  poison_amount = v["poison_amount"].to_int(8);
268  rest_heal_amount = v["rest_heal_amount"].to_int(2);
269  recall_cost = v["recall_cost"].to_int(20);
270  kill_experience = v["kill_experience"].to_int(8);
271  combat_experience= v["combat_experience"].to_int(1);
272  lobby_refresh = v["lobby_refresh"].to_int(2000);
273  default_terrain = v["default_terrain"].str();
274  tile_size = v["tile_size"].to_int(72);
275 
276  std::vector<std::string> zoom_levels_str = utils::split(v["zoom_levels"]);
277  if(!zoom_levels_str.empty()) {
278  zoom_levels.clear();
279  std::transform(zoom_levels_str.begin(), zoom_levels_str.end(), std::back_inserter(zoom_levels), [](const std::string zoom) {
280  int z = std::stoi(zoom);
281  if((z / 4) * 4 != z) {
282  ERR_NG << "zoom level " << z << " is not divisible by 4."
283  << " This will cause graphical glitches!";
284  }
285  return z;
286  });
287  }
288 
289  title_music = v["title_music"].str();
290  lobby_music = v["lobby_music"].str();
291 
292  default_victory_music = utils::split(v["default_victory_music"].str());
293  default_defeat_music = utils::split(v["default_defeat_music"].str());
294 
295  if(auto i = v.optional_child("colors")){
296  using namespace game_config::colors;
297 
298  moved_orb_color = i["moved_orb_color"].str();
299  unmoved_orb_color = i["unmoved_orb_color"].str();
300  partial_orb_color = i["partial_orb_color"].str();
301  enemy_orb_color = i["enemy_orb_color"].str();
302  ally_orb_color = i["ally_orb_color"].str();
303  } // colors
304 
305  show_ally_orb = v["show_ally_orb"].to_bool(true);
306  show_enemy_orb = v["show_enemy_orb"].to_bool(false);
307  show_moved_orb = v["show_moved_orb"].to_bool(true);
308  show_partial_orb = v["show_partial_orb"].to_bool(true);
309  show_status_on_ally_orb = v["show_status_on_ally_orb"].to_bool(true);
310  show_unmoved_orb = v["show_unmoved_orb"].to_bool(true);
311  show_disengaged_orb = v["show_disengaged_orb"].to_bool(true);
312 
313  if(auto i = v.optional_child("images")){
314  using namespace game_config::images;
315 
316  if (!i["game_title_background"].blank()) {
317  // Select a background at random
318  const auto backgrounds = utils::split(i["game_title_background"].str());
319  if (backgrounds.size() > 1) {
320  int r = rand() % (backgrounds.size());
321  game_title_background = backgrounds.at(r);
322  } else if (backgrounds.size() == 1) {
323  game_title_background = backgrounds.at(0);
324  }
325  }
326 
327  // Allow game_title to be empty
328  game_title = i["game_title"].str();
329  game_logo = i["game_logo"].str();
330  game_logo_background = i["game_logo_background"].str();
331 
332  victory_laurel = i["victory_laurel"].str();
333  victory_laurel_hardest = i["victory_laurel_hardest"].str();
334  victory_laurel_easy = i["victory_laurel_easy"].str();
335 
336  orb = i["orb"].str();
337  orb_two_color = i["orb_two_color"].str();
338  energy = i["energy"].str();
339 
340  battery_icon = i["battery_icon"].str();
341  time_icon = i["time_icon"].str();
342 
343  flag = i["flag"].str();
344  flag_icon = i["flag_icon"].str();
345 
346  terrain_mask = i["terrain_mask"].str();
347  grid_top = i["grid_top"].str();
348  grid_bottom = i["grid_bottom"].str();
349  mouseover = i["mouseover"].str();
350  selected = i["selected"].str();
351  editor_brush = i["editor_brush"].str();
352  linger = i["linger"].str();
353 
354  observer = i["observer"].str();
355  tod_bright = i["tod_bright"].str();
356  tod_dark = i["tod_dark"].str();
357  level = i["level"].str();
358  ellipsis = i["ellipsis"].str();
359  missing = i["missing"].str();
360  blank = i["blank"].str();
361  } // images
362 
363  hp_bar_scaling = v["hp_bar_scaling"].to_double(0.666);
364  xp_bar_scaling = v["xp_bar_scaling"].to_double(0.5);
365 
366  foot_speed_prefix = utils::split(v["footprint_prefix"]);
367  foot_teleport_enter = v["footprint_teleport_enter"].str();
368  foot_teleport_exit = v["footprint_teleport_exit"].str();
369 
370  shroud_prefix = v["shroud_prefix"].str();
371  fog_prefix = v["fog_prefix"].str();
372  reach_map_prefix = v["reach_map_prefix"].str();
373 
375 
376  if(const config::attribute_value* a = v.get("flag_rgb")) {
377  flag_rgb = a->str();
378  }
379 
380  if(const config::attribute_value* a = v.get("unit_rgb")) {
381  unit_rgb = a->str();
382  }
383 
384  const auto parse_config_color_list = [&](
385  const std::string& key,
386  const color_t fallback)->std::vector<color_t>
387  {
388  std::vector<color_t> color_vec;
389 
390  for(const auto& s : utils::split(v[key].str())) {
391  try {
392  color_vec.push_back(color_t::from_hex_string(s));
393  } catch(const std::invalid_argument& e) {
394  ERR_NG << "Error parsing color list '" << key << "'.\n" << e.what();
395  color_vec.push_back(fallback);
396  }
397  }
398 
399  return color_vec;
400  };
401 
402  red_green_scale = parse_config_color_list("red_green_scale", {255, 255, 255});
403  red_green_scale_text = parse_config_color_list("red_green_scale_text", {255, 255, 255});
404  blue_white_scale = parse_config_color_list("blue_white_scale", {0 , 0 , 255});
405  blue_white_scale_text = parse_config_color_list("blue_white_scale_text", {0 , 0 , 255});
406 
407  server_list.clear();
408 
409  for(const config& server : v.child_range("server")) {
410  server_info sinf;
411  sinf.name = server["name"].str();
412  sinf.address = server["address"].str();
413  server_list.push_back(sinf);
414  }
415 
416  if(auto s = v.optional_child("sounds")) {
417  using namespace game_config::sounds;
418 
419  const auto load_attribute = [](const config& c, const std::string& key, std::string& member) {
420  if(c.has_attribute(key)) {
421  member = c[key].str();
422  }
423  };
424 
425  load_attribute(*s, "turn_bell", turn_bell);
426  load_attribute(*s, "timer_bell", timer_bell);
427  load_attribute(*s, "public_message", public_message);
428  load_attribute(*s, "private_message", private_message);
429  load_attribute(*s, "friend_message", friend_message);
430  load_attribute(*s, "server_message", server_message);
431  load_attribute(*s, "player_joins", player_joins);
432  load_attribute(*s, "player_leaves", player_leaves);
433  load_attribute(*s, "game_created", game_created);
434  load_attribute(*s, "game_user_arrive", game_user_arrive);
435  load_attribute(*s, "game_user_leave", game_user_leave);
436  load_attribute(*s, "ready_for_start", ready_for_start);
437  load_attribute(*s, "game_has_begun", game_has_begun);
438 
439  if(auto ss = s->optional_child("status")) {
440  using namespace game_config::sounds::status;
441 
442  load_attribute(*ss, "poisoned", poisoned);
443  load_attribute(*ss, "slowed", slowed);
444  load_attribute(*ss, "petrified", petrified);
445  }
446  }
447 }
448 
449 void add_color_info(const game_config_view& v, bool build_defaults)
450 {
451  if(build_defaults) {
452  default_colors.clear();
453  }
454 
455  for(const config& teamC : v.child_range("color_range")) {
456  const config::attribute_value* a1 = teamC.get("id"), *a2 = teamC.get("rgb");
457  if(!a1 || !a2) {
458  continue;
459  }
460 
461  std::string id = *a1;
462  std::vector<color_t> temp;
463 
464  for(const auto& s : utils::split(*a2)) {
465  try {
466  temp.push_back(color_t::from_hex_string(s));
467  } catch(const std::invalid_argument&) {
468  std::stringstream ss;
469  ss << "can't parse color string:\n" << teamC.debug() << "\n";
470  throw config::error(ss.str());
471  }
472  }
473 
474  team_rgb_range.emplace(id, color_range(temp));
475  team_rgb_name.emplace(id, teamC["name"].t_str());
476 
477  LOG_NG << "registered color range '" << id << "': " << team_rgb_range[id].debug();
478 
479  // Generate palette of same name;
480  team_rgb_colors.emplace(id, palette(team_rgb_range[id]));
481 
482  if(build_defaults && teamC["default"].to_bool()) {
483  default_colors.push_back(*a1);
484  }
485  }
486 
487  for(const config &cp : v.child_range("color_palette")) {
488  for(const auto& [key, value] : cp.attribute_range()) {
489  std::vector<color_t> temp;
490  for(const auto& s : utils::split(value)) {
491  try {
492  temp.push_back(color_t::from_hex_string(s));
493  } catch(const std::invalid_argument& e) {
494  ERR_NG << "Invalid color in palette: " << s << " (" << e.what() << ")";
495  }
496  }
497 
498  team_rgb_colors.emplace(key, temp);
499  LOG_NG << "registered color palette: " << key;
500  }
501  }
502 }
503 
505 {
506  default_colors.clear();
507  team_rgb_colors.clear();
508  team_rgb_name.clear();
509  team_rgb_range.clear();
510 }
511 
512 const color_range& color_info(std::string_view name)
513 {
514  auto i = team_rgb_range.find(name);
515  if(i != team_rgb_range.end()) {
516  return i->second;
517  }
518 
519  std::vector<color_t> temp;
520  for(const auto& s : utils::split(name)) {
521  try {
522  temp.push_back(color_t::from_hex_string(s));
523  } catch(const std::invalid_argument&) {
524  throw config::error(_("Invalid color in range: ") + s);
525  }
526  }
527 
528  team_rgb_range.emplace(name, color_range(temp));
529  return color_info(name);
530 }
531 
532 const std::vector<color_t>& tc_info(std::string_view name)
533 {
534  auto i = team_rgb_colors.find(name);
535  if(i != team_rgb_colors.end()) {
536  return i->second;
537  }
538 
539  std::vector<color_t> temp;
540  for(const auto& s : utils::split(name)) {
541  try {
542  temp.push_back(color_t::from_hex_string(s));
543  } catch(const std::invalid_argument& e) {
544  static std::vector<color_t> stv;
545  ERR_NG << "Invalid color in palette: " << s << " (" << e.what() << ")";
546  return stv;
547  }
548  }
549 
550  team_rgb_colors.emplace(name, temp);
551  return tc_info(name);
552 }
553 
554 color_t red_to_green(double val, bool for_text)
555 {
556  const std::vector<color_t>& color_scale = for_text ? red_green_scale_text : red_green_scale;
557 
558  const double val_scaled = std::clamp(0.01 * val, 0.0, 1.0);
559  const int lvl = int(std::nearbyint((color_scale.size() - 1) * val_scaled));
560 
561  return color_scale[lvl];
562 }
563 
564 color_t blue_to_white(double val, bool for_text)
565 {
566  const std::vector<color_t>& color_scale = for_text ? blue_white_scale_text : blue_white_scale;
567 
568  const double val_scaled = std::clamp(0.01 * val, 0.0, 1.0);
569  const int lvl = int(std::nearbyint((color_scale.size() - 1) * val_scaled));
570 
571  return color_scale[lvl];
572 }
573 
575 {
576  std::string ret = _("The Battle for Wesnoth") + " - " + revision;
577  return ret;
578 }
579 
580 } // game_config
A color range definition is made of four reference RGB colors, used for calculating conversions from ...
Definition: color_range.hpp:49
Variant for storing WML attributes.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:172
child_itors child_range(config_key_type key)
Definition: config.cpp:272
A class grating read only view to a vector of config objects, viewed as one config with all children ...
static game_config_view wrap(const config &cfg)
config_array_view child_range(config_key_type key) const
std::vector< color_t > palette(const color_range &cr)
Creates a reference color palette from a color range.
Definitions for the interface to Wesnoth Markup Language (WML).
std::size_t i
Definition: function.cpp:1023
static lg::log_domain log_engine("engine")
#define ERR_NG
Definition: game_config.cpp:30
#define LOG_NG
Definition: game_config.cpp:29
Interfaces for manipulating version numbers of engine, add-ons, etc.
static std::string _(const char *str)
Definition: gettext.hpp:93
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:198
Standard logging facilities (interface).
Manage the empty-palette in the editor.
Definition: action.cpp:31
std::string partial_orb_color
std::string moved_orb_color
std::string unmoved_orb_color
std::string ally_orb_color
std::string enemy_orb_color
std::string default_color_list
std::string selected_menu
std::string terrain_mask
std::string victory_laurel_hardest
std::string victory_laurel
std::string orb_two_color
std::string tod_bright
std::string time_icon
std::string selected
std::string app_icon
std::string deselected_menu
std::string observer
std::string ellipsis
std::string unchecked_menu
std::string game_title_background
std::string grid_top
std::string grid_bottom
std::string game_title
std::string game_logo
std::string game_logo_background
std::string wml_menu
std::string flag_icon
std::string editor_brush
std::string checked_menu
std::string mouseover
std::string battery_icon
std::string victory_laurel_easy
std::string tod_dark
std::string public_message
std::string private_message
const std::string menu_expand
std::string player_leaves
std::string server_message
std::string game_user_arrive
std::string turn_bell
const std::string menu_contract
std::string game_user_leave
const std::string checkbox_release
std::string timer_bell
std::string friend_message
const std::string menu_select
std::string ready_for_start
std::string game_has_begun
const std::string button_press
std::string game_created
std::string player_joins
const std::string slider_adjust
Game configuration data as global variables.
Definition: build_info.cpp:61
int rest_heal_amount
Definition: game_config.cpp:45
std::vector< std::string > default_defeat_music
std::map< std::string, color_range, std::less<> > team_rgb_range
Colors defined by WML [color_range] tags.
std::string flag_rgb
color_t blue_to_white(double val, bool for_text)
std::map< std::string, t_string, std::less<> > team_rgb_name
const std::size_t max_loop
The maximum number of hexes on a map and items in an array and also used as maximum in wml loops.
Definition: game_config.cpp:71
std::string get_default_title_string()
bool ignore_replay_errors
Definition: game_config.cpp:85
std::string foot_teleport_enter
int village_income
Definition: game_config.cpp:38
bool show_status_on_ally_orb
std::vector< color_t > red_green_scale_text
bool show_moved_orb
std::vector< std::string > foot_speed_prefix
const int gold_carryover_percentage
Default percentage gold carried over to the next scenario.
Definition: game_config.cpp:47
std::string foot_teleport_exit
std::string lobby_music
const std::vector< color_t > & tc_info(std::string_view name)
std::string fog_prefix
Definition: game_config.cpp:55
const bool & debug
Definition: game_config.cpp:92
const color_range & color_info(std::string_view name)
std::vector< std::string > default_colors
std::vector< color_t > blue_white_scale
std::string shroud_prefix
Definition: game_config.cpp:55
double hp_bar_scaling
Definition: game_config.cpp:62
bool disable_autosave
Definition: game_config.cpp:89
std::vector< server_info > server_list
Definition: game_config.cpp:73
int kill_experience
Definition: game_config.cpp:41
std::string unit_rgb
unsigned int tile_size
Definition: game_config.cpp:52
bool show_ally_orb
bool allow_insecure
Definition: game_config.cpp:75
static void add_color_info(const game_config_view &v, bool build_defaults)
bool show_disengaged_orb
std::vector< unsigned int > zoom_levels
Definition: game_config.cpp:57
bool show_partial_orb
std::string title_music
std::string default_terrain
Definition: game_config.cpp:54
bool show_enemy_orb
unsigned lobby_network_timer
Definition: game_config.cpp:68
bool show_unmoved_orb
unsigned lobby_refresh
Definition: game_config.cpp:69
std::string reach_map_prefix
Definition: game_config.cpp:55
std::vector< std::string > default_victory_music
int combat_experience
Definition: game_config.cpp:42
void reset_color_info()
const std::string revision
void set_debug(bool new_debug)
Definition: game_config.cpp:94
std::vector< color_t > red_green_scale
bool exit_at_end
Definition: game_config.cpp:87
std::vector< color_t > blue_white_scale_text
void load_config(const config &v)
color_t red_to_green(double val, bool for_text)
Return a color corresponding to the value val red for val=0.0 to green for val=100....
std::map< std::string, std::vector< color_t >, std::less<> > team_rgb_colors
double xp_bar_scaling
Definition: game_config.cpp:63
int village_support
Definition: game_config.cpp:39
bool get_log_domain_severity(const std::string &name, severity &severity)
Definition: log.cpp:369
severity
Definition: log.hpp:83
bool set_log_domain_severity(const std::string &name, severity severity)
Definition: log.cpp:345
std::vector< std::string > split(const config_attribute_value &val)
The basic class for representing 8-bit RGB or RGBA colour values.
Definition: color.hpp:59
static color_t from_hex_string(std::string_view c)
Creates a new color_t object from a string variable in hex format.
Definition: color.cpp:64
mock_char c
static map_location::direction s
#define e