The Battle for Wesnoth  1.19.1+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 static std::vector<color_t> blue_white_scale;
142 static 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
199  // GUI elements
203  // TODO: de-hardcode this
204  selected_menu = "buttons/radiobox-pressed.png",
205  deselected_menu = "buttons/radiobox.png",
206  checked_menu = "buttons/checkbox-pressed.png",
207  unchecked_menu = "buttons/checkbox.png",
208  wml_menu = "buttons/WML-custom.png",
213  // notifications icon
214  app_icon = "images/icons/icon-game.png";
215 
216 } //images
217 
218 //
219 // Sound constants
220 //
221 namespace sounds {
222 
223 std::string
224  turn_bell = "bell.wav",
225  timer_bell = "timer.wav",
226  public_message = "chat-[1~3].ogg",
227  private_message = "chat-highlight.ogg",
228  friend_message = "chat-friend.ogg",
229  server_message = "receive.wav",
230  player_joins = "arrive.wav",
231  player_leaves = "leave.wav",
232  game_user_arrive = "join.wav",
233  game_user_leave = "leave.wav",
234  ready_for_start = "bell.wav",
235  game_has_begun = "gamestart.ogg",
236  game_created = "join.wav";
237 
238 const std::string
239  button_press = "button.wav",
240  checkbox_release = "checkbox.wav",
241  slider_adjust = "slider.wav",
242  menu_expand = "expand.wav",
243  menu_contract = "contract.wav",
244  menu_select = "select.wav";
245 
246 namespace status {
247 
248 std::string
249  poisoned = "poison.ogg",
250  slowed = "slowed.wav",
251  petrified = "petrified.ogg";
252 
253 } // status
254 
255 } // sounds
256 
257 static void add_color_info(const game_config_view& v, bool build_defaults);
259 {
260  add_color_info(v, false);
261 }
262 
263 void load_config(const config &v)
264 {
265  base_income = v["base_income"].to_int(2);
266  village_income = v["village_income"].to_int(1);
267  village_support = v["village_support"].to_int(1);
268  poison_amount = v["poison_amount"].to_int(8);
269  rest_heal_amount = v["rest_heal_amount"].to_int(2);
270  recall_cost = v["recall_cost"].to_int(20);
271  kill_experience = v["kill_experience"].to_int(8);
272  combat_experience= v["combat_experience"].to_int(1);
273  lobby_refresh = v["lobby_refresh"].to_int(2000);
274  default_terrain = v["default_terrain"].str();
275  tile_size = v["tile_size"].to_int(72);
276 
277  std::vector<std::string> zoom_levels_str = utils::split(v["zoom_levels"]);
278  if(!zoom_levels_str.empty()) {
279  zoom_levels.clear();
280  std::transform(zoom_levels_str.begin(), zoom_levels_str.end(), std::back_inserter(zoom_levels), [](const std::string zoom) {
281  int z = std::stoi(zoom);
282  if((z / 4) * 4 != z) {
283  ERR_NG << "zoom level " << z << " is not divisible by 4."
284  << " This will cause graphical glitches!";
285  }
286  return z;
287  });
288  }
289 
290  title_music = v["title_music"].str();
291  lobby_music = v["lobby_music"].str();
292 
293  default_victory_music = utils::split(v["default_victory_music"].str());
294  default_defeat_music = utils::split(v["default_defeat_music"].str());
295 
296  if(auto i = v.optional_child("colors")){
297  using namespace game_config::colors;
298 
299  moved_orb_color = i["moved_orb_color"].str();
300  unmoved_orb_color = i["unmoved_orb_color"].str();
301  partial_orb_color = i["partial_orb_color"].str();
302  enemy_orb_color = i["enemy_orb_color"].str();
303  ally_orb_color = i["ally_orb_color"].str();
304  } // colors
305 
306  show_ally_orb = v["show_ally_orb"].to_bool(true);
307  show_enemy_orb = v["show_enemy_orb"].to_bool(false);
308  show_moved_orb = v["show_moved_orb"].to_bool(true);
309  show_partial_orb = v["show_partial_orb"].to_bool(true);
310  show_status_on_ally_orb = v["show_status_on_ally_orb"].to_bool(true);
311  show_unmoved_orb = v["show_unmoved_orb"].to_bool(true);
312  show_disengaged_orb = v["show_disengaged_orb"].to_bool(true);
313 
314  if(auto i = v.optional_child("images")){
315  using namespace game_config::images;
316 
317  if (!i["game_title_background"].blank()) {
318  // Select a background at random
319  const auto backgrounds = utils::split(i["game_title_background"].str());
320  if (backgrounds.size() > 1) {
321  int r = rand() % (backgrounds.size());
322  game_title_background = backgrounds.at(r);
323  } else if (backgrounds.size() == 1) {
324  game_title_background = backgrounds.at(0);
325  }
326  }
327 
328  // Allow game_title to be empty
329  game_title = i["game_title"].str();
330  game_logo = i["game_logo"].str();
331  game_logo_background = i["game_logo_background"].str();
332 
333  victory_laurel = i["victory_laurel"].str();
334  victory_laurel_hardest = i["victory_laurel_hardest"].str();
335  victory_laurel_easy = i["victory_laurel_easy"].str();
336 
337  orb = i["orb"].str();
338  orb_two_color = i["orb_two_color"].str();
339  energy = i["energy"].str();
340 
341  battery_icon = i["battery_icon"].str();
342  time_icon = i["time_icon"].str();
343 
344  flag = i["flag"].str();
345  flag_icon = i["flag_icon"].str();
346 
347  terrain_mask = i["terrain_mask"].str();
348  grid_top = i["grid_top"].str();
349  grid_bottom = i["grid_bottom"].str();
350  mouseover = i["mouseover"].str();
351  selected = i["selected"].str();
352  editor_brush = i["editor_brush"].str();
353  unreachable = i["unreachable"].str();
354  linger = i["linger"].str();
355 
356  observer = i["observer"].str();
357  tod_bright = i["tod_bright"].str();
358  tod_dark = i["tod_dark"].str();
359  level = i["level"].str();
360  ellipsis = i["ellipsis"].str();
361  missing = i["missing"].str();
362  blank = i["blank"].str();
363  } // images
364 
365  hp_bar_scaling = v["hp_bar_scaling"].to_double(0.666);
366  xp_bar_scaling = v["xp_bar_scaling"].to_double(0.5);
367 
368  foot_speed_prefix = utils::split(v["footprint_prefix"]);
369  foot_teleport_enter = v["footprint_teleport_enter"].str();
370  foot_teleport_exit = v["footprint_teleport_exit"].str();
371 
372  shroud_prefix = v["shroud_prefix"].str();
373  fog_prefix = v["fog_prefix"].str();
374 
376 
377  if(const config::attribute_value* a = v.get("flag_rgb")) {
378  flag_rgb = a->str();
379  }
380 
381  if(const config::attribute_value* a = v.get("unit_rgb")) {
382  unit_rgb = a->str();
383  }
384 
385  const auto parse_config_color_list = [&](
386  const std::string& key,
387  const color_t fallback)->std::vector<color_t>
388  {
389  std::vector<color_t> color_vec;
390 
391  for(const auto& s : utils::split(v[key].str())) {
392  try {
393  color_vec.push_back(color_t::from_hex_string(s));
394  } catch(const std::invalid_argument& e) {
395  ERR_NG << "Error parsing color list '" << key << "'.\n" << e.what();
396  color_vec.push_back(fallback);
397  }
398  }
399 
400  return color_vec;
401  };
402 
403  red_green_scale = parse_config_color_list("red_green_scale", {255, 255, 255});
404  red_green_scale_text = parse_config_color_list("red_green_scale_text", {255, 255, 255});
405  blue_white_scale = parse_config_color_list("blue_white_scale", {0 , 0 , 255});
406  blue_white_scale_text = parse_config_color_list("blue_white_scale_text", {0 , 0 , 255});
407 
408  server_list.clear();
409 
410  for(const config& server : v.child_range("server")) {
411  server_info sinf;
412  sinf.name = server["name"].str();
413  sinf.address = server["address"].str();
414  server_list.push_back(sinf);
415  }
416 
417  if(auto s = v.optional_child("sounds")) {
418  using namespace game_config::sounds;
419 
420  const auto load_attribute = [](const config& c, const std::string& key, std::string& member) {
421  if(c.has_attribute(key)) {
422  member = c[key].str();
423  }
424  };
425 
426  load_attribute(*s, "turn_bell", turn_bell);
427  load_attribute(*s, "timer_bell", timer_bell);
428  load_attribute(*s, "public_message", public_message);
429  load_attribute(*s, "private_message", private_message);
430  load_attribute(*s, "friend_message", friend_message);
431  load_attribute(*s, "server_message", server_message);
432  load_attribute(*s, "player_joins", player_joins);
433  load_attribute(*s, "player_leaves", player_leaves);
434  load_attribute(*s, "game_created", game_created);
435  load_attribute(*s, "game_user_arrive", game_user_arrive);
436  load_attribute(*s, "game_user_leave", game_user_leave);
437  load_attribute(*s, "ready_for_start", ready_for_start);
438  load_attribute(*s, "game_has_begun", game_has_begun);
439 
440  if(auto ss = s->optional_child("status")) {
441  using namespace game_config::sounds::status;
442 
443  load_attribute(*ss, "poisoned", poisoned);
444  load_attribute(*ss, "slowed", slowed);
445  load_attribute(*ss, "petrified", petrified);
446  }
447  }
448 }
449 
450 void add_color_info(const game_config_view& v, bool build_defaults)
451 {
452  if(build_defaults) {
453  default_colors.clear();
454  }
455 
456  for(const config& teamC : v.child_range("color_range")) {
457  const config::attribute_value* a1 = teamC.get("id"), *a2 = teamC.get("rgb");
458  if(!a1 || !a2) {
459  continue;
460  }
461 
462  std::string id = *a1;
463  std::vector<color_t> temp;
464 
465  for(const auto& s : utils::split(*a2)) {
466  try {
467  temp.push_back(color_t::from_hex_string(s));
468  } catch(const std::invalid_argument&) {
469  std::stringstream ss;
470  ss << "can't parse color string:\n" << teamC.debug() << "\n";
471  throw config::error(ss.str());
472  }
473  }
474 
475  team_rgb_range.emplace(id, color_range(temp));
476  team_rgb_name.emplace(id, teamC["name"].t_str());
477 
478  LOG_NG << "registered color range '" << id << "': " << team_rgb_range[id].debug();
479 
480  // Ggenerate palette of same name;
481  std::vector<color_t> tp = palette(team_rgb_range[id]);
482  if(!tp.empty()) {
483  team_rgb_colors.emplace(id, tp);
484  }
485 
486  if(build_defaults && teamC["default"].to_bool()) {
487  default_colors.push_back(*a1);
488  }
489  }
490 
491  for(const config &cp : v.child_range("color_palette")) {
492  for(const config::attribute& rgb : cp.attribute_range()) {
493  std::vector<color_t> temp;
494  for(const auto& s : utils::split(rgb.second)) {
495  try {
496  temp.push_back(color_t::from_hex_string(s));
497  } catch(const std::invalid_argument& e) {
498  ERR_NG << "Invalid color in palette: " << s << " (" << e.what() << ")";
499  }
500  }
501 
502  team_rgb_colors.emplace(rgb.first, temp);
503  LOG_NG << "registered color palette: " << rgb.first;
504  }
505  }
506 }
507 
509 {
510  default_colors.clear();
511  team_rgb_colors.clear();
512  team_rgb_name.clear();
513  team_rgb_range.clear();
514 }
515 
516 const color_range& color_info(std::string_view name)
517 {
518  auto i = team_rgb_range.find(name);
519  if(i != team_rgb_range.end()) {
520  return i->second;
521  }
522 
523  std::vector<color_t> temp;
524  for(const auto& s : utils::split(name)) {
525  try {
526  temp.push_back(color_t::from_hex_string(s));
527  } catch(const std::invalid_argument&) {
528  throw config::error(_("Invalid color in range: ") + s);
529  }
530  }
531 
532  team_rgb_range.emplace(name, color_range(temp));
533  return color_info(name);
534 }
535 
536 const std::vector<color_t>& tc_info(std::string_view name)
537 {
538  auto i = team_rgb_colors.find(name);
539  if(i != team_rgb_colors.end()) {
540  return i->second;
541  }
542 
543  std::vector<color_t> temp;
544  for(const auto& s : utils::split(name)) {
545  try {
546  temp.push_back(color_t::from_hex_string(s));
547  } catch(const std::invalid_argument& e) {
548  static std::vector<color_t> stv;
549  ERR_NG << "Invalid color in palette: " << s << " (" << e.what() << ")";
550  return stv;
551  }
552  }
553 
554  team_rgb_colors.emplace(name, temp);
555  return tc_info(name);
556 }
557 
558 color_t red_to_green(double val, bool for_text)
559 {
560  const std::vector<color_t>& color_scale = for_text ? red_green_scale_text : red_green_scale;
561 
562  const double val_scaled = std::clamp(0.01 * val, 0.0, 1.0);
563  const int lvl = int(std::nearbyint((color_scale.size() - 1) * val_scaled));
564 
565  return color_scale[lvl];
566 }
567 
568 color_t blue_to_white(double val, bool for_text)
569 {
570  const std::vector<color_t>& color_scale = for_text ? blue_white_scale_text : blue_white_scale;
571 
572  const double val_scaled = std::clamp(0.01 * val, 0.0, 1.0);
573  const int lvl = int(std::nearbyint((color_scale.size() - 1) * val_scaled));
574 
575  return color_scale[lvl];
576 }
577 
579 {
580  std::string ret = _("The Battle for Wesnoth") + " - " + revision;
581  return ret;
582 }
583 
584 } // 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:159
child_itors child_range(config_key_type key)
Definition: config.cpp:273
attribute_map::value_type attribute
Definition: config.hpp:299
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.
std::size_t i
Definition: function.cpp:968
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:205
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 unreachable
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)
static std::vector< color_t > blue_white_scale_text
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::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
static std::vector< color_t > blue_white_scale
bool show_unmoved_orb
unsigned lobby_refresh
Definition: game_config.cpp:69
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
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:368
severity
Definition: log.hpp:82
bool set_log_domain_severity(const std::string &name, severity severity)
Definition: log.cpp:344
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(const std::string &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
#define a