The Battle for Wesnoth  1.19.8+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"
23 #include "serialization/chrono.hpp"
25 
26 #include <cmath>
27 #include <random>
28 
29 static lg::log_domain log_engine("engine");
30 #define LOG_NG LOG_STREAM(info, log_engine)
31 #define ERR_NG LOG_STREAM(err, log_engine)
32 
33 using namespace std::chrono_literals;
34 
35 namespace game_config
36 {
37 //
38 // Gameplay constants
39 //
40 int base_income = 2;
43 int recall_cost = 20;
46 
47 int poison_amount = 8;
49 
51 
52 //
53 // Terrain-related constants
54 //
55 unsigned int tile_size = 72;
56 
57 std::string default_terrain;
59 
60 std::vector<unsigned int> zoom_levels {36, 72, 144};
61 
62 //
63 // Display scale constants
64 //
65 double hp_bar_scaling = 0.666;
66 double xp_bar_scaling = 0.5;
67 
68 //
69 // Misc
70 //
71 std::chrono::milliseconds lobby_network_timer = 100ms;
72 std::chrono::milliseconds lobby_refresh = 4000ms;
73 
74 const std::size_t max_loop = 65536;
75 
76 std::vector<server_info> server_list;
77 
78 bool allow_insecure = false;
79 bool addon_server_info = false;
80 
81 //
82 // Gamestate flags
83 //
84 bool
85  debug_impl = false,
86  debug_lua = false,
87  strict_lua = false,
88  editor = false,
90  mp_debug = false,
91  exit_at_end = false,
93  no_addons = false;
94 
95 const bool& debug = debug_impl;
96 
97 void set_debug(bool new_debug) {
98  // TODO: remove severity static casts and fix issue #7894
99  if(debug_impl && !new_debug) {
100  // Turning debug mode off; decrease deprecation severity
102  if(lg::get_log_domain_severity("deprecation", severity)) {
103  int severityInt = static_cast<int>(severity);
104  lg::set_log_domain_severity("deprecation", static_cast<lg::severity>(severityInt - 2));
105  }
106  } else if(!debug_impl && new_debug) {
107  // Turning debug mode on; increase deprecation severity
109  if(lg::get_log_domain_severity("deprecation", severity)) {
110  int severityInt = static_cast<int>(severity);
111  lg::set_log_domain_severity("deprecation", static_cast<lg::severity>(severityInt + 2));
112  }
113  }
114  debug_impl = new_debug;
115 }
116 
117 //
118 // Orb display flags
119 //
127 
128 //
129 // Music constants
130 //
132 
133 std::vector<std::string> default_defeat_music;
134 std::vector<std::string> default_victory_music;
135 
136 //
137 // Color info
138 //
139 std::string flag_rgb, unit_rgb;
140 
141 std::vector<color_t> red_green_scale;
142 std::vector<color_t> red_green_scale_text;
143 
144 std::vector<color_t> blue_white_scale;
145 std::vector<color_t> blue_white_scale_text;
146 
147 std::map<std::string, color_range, std::less<>> team_rgb_range;
148 // Map [color_range]id to [color_range]name, or "" if no name
149 std::map<std::string, t_string, std::less<>> team_rgb_name;
150 
151 std::map<std::string, std::vector<color_t>, std::less<>> team_rgb_colors;
152 
153 std::vector<std::string> default_colors;
154 
155 namespace colors
156 {
157 std::string ally_orb_color;
158 std::string enemy_orb_color;
159 std::string moved_orb_color;
160 std::string partial_orb_color;
161 std::string unmoved_orb_color;
162 std::string default_color_list;
163 } // namespace colors
164 
165 //
166 // Image constants
167 //
168 std::vector<std::string> foot_speed_prefix;
169 
171 std::string foot_teleport_exit;
172 
173 namespace images {
174 
175 std::string
183  // orbs and hp/xp bar
187  // top bar icons
190  // flags
193  // hex overlay
201  // GUI elements
205  // TODO: de-hardcode this
206  selected_menu = "buttons/radiobox-pressed.png",
207  deselected_menu = "buttons/radiobox.png",
208  checked_menu = "buttons/checkbox-pressed.png",
209  unchecked_menu = "buttons/checkbox.png",
210  wml_menu = "buttons/WML-custom.png",
215  // notifications icon
216  app_icon = "images/icons/icon-game.png";
217 
218 } //images
219 
220 //
221 // Sound constants
222 //
223 namespace sounds {
224 
225 std::string
226  turn_bell = "bell.wav",
227  timer_bell = "timer.wav",
228  public_message = "chat-[1~3].ogg",
229  private_message = "chat-highlight.ogg",
230  friend_message = "chat-friend.ogg",
231  server_message = "receive.wav",
232  player_joins = "arrive.wav",
233  player_leaves = "leave.wav",
234  game_user_arrive = "join.wav",
235  game_user_leave = "leave.wav",
236  ready_for_start = "bell.wav",
237  game_has_begun = "gamestart.ogg",
238  game_created = "join.wav";
239 
240 const std::string
241  button_press = "button.wav",
242  checkbox_release = "checkbox.wav",
243  slider_adjust = "slider.wav",
244  menu_expand = "expand.wav",
245  menu_contract = "contract.wav",
246  menu_select = "select.wav";
247 
248 namespace status {
249 
250 std::string
251  poisoned = "poison.ogg",
252  slowed = "slowed.wav",
253  petrified = "petrified.ogg";
254 
255 } // status
256 
257 } // sounds
258 
259 static void add_color_info(const game_config_view& v, bool build_defaults);
261 {
262  add_color_info(v, false);
263 }
264 
265 void load_config(const config &v)
266 {
267  base_income = v["base_income"].to_int(2);
268  village_income = v["village_income"].to_int(1);
269  village_support = v["village_support"].to_int(1);
270  poison_amount = v["poison_amount"].to_int(8);
271  rest_heal_amount = v["rest_heal_amount"].to_int(2);
272  recall_cost = v["recall_cost"].to_int(20);
273  kill_experience = v["kill_experience"].to_int(8);
274  combat_experience= v["combat_experience"].to_int(1);
275  lobby_refresh = chrono::parse_duration(v["lobby_refresh"], 2000ms);
276  default_terrain = v["default_terrain"].str();
277  tile_size = v["tile_size"].to_int(72);
278 
279  std::vector<std::string> zoom_levels_str = utils::split(v["zoom_levels"]);
280  if(!zoom_levels_str.empty()) {
281  zoom_levels.clear();
282  std::transform(zoom_levels_str.begin(), zoom_levels_str.end(), std::back_inserter(zoom_levels), [](const std::string& zoom) {
283  int z = std::stoi(zoom);
284  if((z / 4) * 4 != z) {
285  ERR_NG << "zoom level " << z << " is not divisible by 4."
286  << " This will cause graphical glitches!";
287  }
288  return z;
289  });
290  }
291 
292  title_music = v["title_music"].str();
293  lobby_music = v["lobby_music"].str();
294 
295  default_victory_music = utils::split(v["default_victory_music"].str());
296  default_defeat_music = utils::split(v["default_defeat_music"].str());
297 
298  if(auto i = v.optional_child("colors")){
299  using namespace game_config::colors;
300 
301  moved_orb_color = i["moved_orb_color"].str();
302  unmoved_orb_color = i["unmoved_orb_color"].str();
303  partial_orb_color = i["partial_orb_color"].str();
304  enemy_orb_color = i["enemy_orb_color"].str();
305  ally_orb_color = i["ally_orb_color"].str();
306  } // colors
307 
308  show_ally_orb = v["show_ally_orb"].to_bool(true);
309  show_enemy_orb = v["show_enemy_orb"].to_bool(false);
310  show_moved_orb = v["show_moved_orb"].to_bool(true);
311  show_partial_orb = v["show_partial_orb"].to_bool(true);
312  show_status_on_ally_orb = v["show_status_on_ally_orb"].to_bool(true);
313  show_unmoved_orb = v["show_unmoved_orb"].to_bool(true);
314  show_disengaged_orb = v["show_disengaged_orb"].to_bool(true);
315 
316  if(auto i = v.optional_child("images")){
317  using namespace game_config::images;
318 
319  if (!i["game_title_background"].blank()) {
320  // Select a background at random
321  const auto backgrounds = utils::split(i["game_title_background"].str());
322  if (backgrounds.size() > 1) {
323  int r = rand() % (backgrounds.size());
324  game_title_background = backgrounds.at(r);
325  } else if (backgrounds.size() == 1) {
326  game_title_background = backgrounds.at(0);
327  }
328  }
329 
330  // Allow game_title to be empty
331  game_title = i["game_title"].str();
332  game_logo = i["game_logo"].str();
333  game_logo_background = i["game_logo_background"].str();
334 
335  victory_laurel = i["victory_laurel"].str();
336  victory_laurel_hardest = i["victory_laurel_hardest"].str();
337  victory_laurel_easy = i["victory_laurel_easy"].str();
338 
339  orb = i["orb"].str();
340  orb_two_color = i["orb_two_color"].str();
341  energy = i["energy"].str();
342 
343  battery_icon = i["battery_icon"].str();
344  time_icon = i["time_icon"].str();
345 
346  flag = i["flag"].str();
347  flag_icon = i["flag_icon"].str();
348 
349  terrain_mask = i["terrain_mask"].str();
350  grid_top = i["grid_top"].str();
351  grid_bottom = i["grid_bottom"].str();
352  mouseover = i["mouseover"].str();
353  selected = i["selected"].str();
354  editor_brush = i["editor_brush"].str();
355  linger = i["linger"].str();
356 
357  observer = i["observer"].str();
358  tod_bright = i["tod_bright"].str();
359  tod_dark = i["tod_dark"].str();
360  level = i["level"].str();
361  ellipsis = i["ellipsis"].str();
362  missing = i["missing"].str();
363  blank = i["blank"].str();
364  } // images
365 
366  hp_bar_scaling = v["hp_bar_scaling"].to_double(0.666);
367  xp_bar_scaling = v["xp_bar_scaling"].to_double(0.5);
368 
369  foot_speed_prefix = utils::split(v["footprint_prefix"]);
370  foot_teleport_enter = v["footprint_teleport_enter"].str();
371  foot_teleport_exit = v["footprint_teleport_exit"].str();
372 
373  shroud_prefix = v["shroud_prefix"].str();
374  fog_prefix = v["fog_prefix"].str();
375  reach_map_prefix = v["reach_map_prefix"].str();
376 
378 
379  if(const config::attribute_value* a = v.get("flag_rgb")) {
380  flag_rgb = a->str();
381  }
382 
383  if(const config::attribute_value* a = v.get("unit_rgb")) {
384  unit_rgb = a->str();
385  }
386 
387  const auto parse_config_color_list = [&](
388  const std::string& key,
389  const color_t fallback)->std::vector<color_t>
390  {
391  std::vector<color_t> color_vec;
392 
393  for(const auto& s : utils::split(v[key].str())) {
394  try {
395  color_vec.push_back(color_t::from_hex_string(s));
396  } catch(const std::invalid_argument& e) {
397  ERR_NG << "Error parsing color list '" << key << "'.\n" << e.what();
398  color_vec.push_back(fallback);
399  }
400  }
401 
402  return color_vec;
403  };
404 
405  red_green_scale = parse_config_color_list("red_green_scale", {255, 255, 255});
406  red_green_scale_text = parse_config_color_list("red_green_scale_text", {255, 255, 255});
407  blue_white_scale = parse_config_color_list("blue_white_scale", {0 , 0 , 255});
408  blue_white_scale_text = parse_config_color_list("blue_white_scale_text", {0 , 0 , 255});
409 
410  server_list.clear();
411 
412  for(const config& server : v.child_range("server")) {
413  server_info sinf;
414  sinf.name = server["name"].str();
415  sinf.address = server["address"].str();
416  server_list.push_back(sinf);
417  }
418 
419  if(auto s = v.optional_child("sounds")) {
420  using namespace game_config::sounds;
421 
422  const auto load_attribute = [](const config& c, const std::string& key, std::string& member) {
423  if(c.has_attribute(key)) {
424  member = c[key].str();
425  }
426  };
427 
428  load_attribute(*s, "turn_bell", turn_bell);
429  load_attribute(*s, "timer_bell", timer_bell);
430  load_attribute(*s, "public_message", public_message);
431  load_attribute(*s, "private_message", private_message);
432  load_attribute(*s, "friend_message", friend_message);
433  load_attribute(*s, "server_message", server_message);
434  load_attribute(*s, "player_joins", player_joins);
435  load_attribute(*s, "player_leaves", player_leaves);
436  load_attribute(*s, "game_created", game_created);
437  load_attribute(*s, "game_user_arrive", game_user_arrive);
438  load_attribute(*s, "game_user_leave", game_user_leave);
439  load_attribute(*s, "ready_for_start", ready_for_start);
440  load_attribute(*s, "game_has_begun", game_has_begun);
441 
442  if(auto ss = s->optional_child("status")) {
443  using namespace game_config::sounds::status;
444 
445  load_attribute(*ss, "poisoned", poisoned);
446  load_attribute(*ss, "slowed", slowed);
447  load_attribute(*ss, "petrified", petrified);
448  }
449  }
450 }
451 
452 void add_color_info(const game_config_view& v, bool build_defaults)
453 {
454  if(build_defaults) {
455  default_colors.clear();
456  }
457 
458  for(const config& teamC : v.child_range("color_range")) {
459  const config::attribute_value* a1 = teamC.get("id"), *a2 = teamC.get("rgb");
460  if(!a1 || !a2) {
461  continue;
462  }
463 
464  std::string id = *a1;
465  std::vector<color_t> temp;
466 
467  for(const auto& s : utils::split(*a2)) {
468  try {
469  temp.push_back(color_t::from_hex_string(s));
470  } catch(const std::invalid_argument&) {
471  std::stringstream ss;
472  ss << "can't parse color string:\n" << teamC.debug() << "\n";
473  throw config::error(ss.str());
474  }
475  }
476 
477  team_rgb_range.emplace(id, color_range(temp));
478  team_rgb_name.emplace(id, teamC["name"].t_str());
479 
480  LOG_NG << "registered color range '" << id << "': " << team_rgb_range[id].debug();
481 
482  // Generate palette of same name;
483  team_rgb_colors.emplace(id, palette(team_rgb_range[id]));
484 
485  if(build_defaults && teamC["default"].to_bool()) {
486  default_colors.push_back(*a1);
487  }
488  }
489 
490  for(const config &cp : v.child_range("color_palette")) {
491  for(const auto& [key, value] : cp.attribute_range()) {
492  std::vector<color_t> temp;
493  for(const auto& s : utils::split(value)) {
494  try {
495  temp.push_back(color_t::from_hex_string(s));
496  } catch(const std::invalid_argument& e) {
497  ERR_NG << "Invalid color in palette: " << s << " (" << e.what() << ")";
498  }
499  }
500 
501  team_rgb_colors.emplace(key, temp);
502  LOG_NG << "registered color palette: " << key;
503  }
504  }
505 }
506 
508 {
509  default_colors.clear();
510  team_rgb_colors.clear();
511  team_rgb_name.clear();
512  team_rgb_range.clear();
513 }
514 
515 const color_range& color_info(std::string_view name)
516 {
517  auto i = team_rgb_range.find(name);
518  if(i != team_rgb_range.end()) {
519  return i->second;
520  }
521 
522  std::vector<color_t> temp;
523  for(const auto& s : utils::split(name)) {
524  try {
525  temp.push_back(color_t::from_hex_string(s));
526  } catch(const std::invalid_argument&) {
527  throw config::error(_("Invalid color in range: ") + s);
528  }
529  }
530 
531  team_rgb_range.emplace(name, color_range(temp));
532  return color_info(name);
533 }
534 
535 const std::vector<color_t>& tc_info(std::string_view name)
536 {
537  auto i = team_rgb_colors.find(name);
538  if(i != team_rgb_colors.end()) {
539  return i->second;
540  }
541 
542  std::vector<color_t> temp;
543  for(const auto& s : utils::split(name)) {
544  try {
545  temp.push_back(color_t::from_hex_string(s));
546  } catch(const std::invalid_argument& e) {
547  static std::vector<color_t> stv;
548  ERR_NG << "Invalid color in palette: " << s << " (" << e.what() << ")";
549  return stv;
550  }
551  }
552 
553  team_rgb_colors.emplace(name, temp);
554  return tc_info(name);
555 }
556 
557 color_t red_to_green(double val, bool for_text)
558 {
559  const std::vector<color_t>& color_scale = for_text ? red_green_scale_text : red_green_scale;
560 
561  const double val_scaled = std::clamp(0.01 * val, 0.0, 1.0);
562  const int lvl = int(std::nearbyint((color_scale.size() - 1) * val_scaled));
563 
564  return color_scale[lvl];
565 }
566 
567 color_t blue_to_white(double val, bool for_text)
568 {
569  const std::vector<color_t>& color_scale = for_text ? blue_white_scale_text : blue_white_scale;
570 
571  const double val_scaled = std::clamp(0.01 * val, 0.0, 1.0);
572  const int lvl = int(std::nearbyint((color_scale.size() - 1) * val_scaled));
573 
574  return color_scale[lvl];
575 }
576 
578 {
579  std::string ret = _("The Battle for Wesnoth") + " - " + revision;
580  return ret;
581 }
582 
583 } // game_config
#define debug(x)
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:158
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).
#define zoom_levels
Definition: display.cpp:86
@ grid_bottom
Bottom half part of grid image.
@ grid_top
Top half part of grid image.
std::size_t i
Definition: function.cpp:1029
static lg::log_domain log_engine("engine")
#define ERR_NG
Definition: game_config.cpp:31
#define LOG_NG
Definition: game_config.cpp:30
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:199
Standard logging facilities (interface).
auto parse_duration(const config_attribute_value &val, const Duration &def=Duration{0})
Definition: chrono.hpp:71
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 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:48
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:74
std::string get_default_title_string()
bool ignore_replay_errors
Definition: game_config.cpp:89
std::string foot_teleport_enter
int village_income
Definition: game_config.cpp:41
bool show_status_on_ally_orb
std::vector< color_t > red_green_scale_text
bool addon_server_info
Definition: game_config.cpp:79
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:50
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:58
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:58
double hp_bar_scaling
Definition: game_config.cpp:65
bool disable_autosave
Definition: game_config.cpp:92
std::vector< server_info > server_list
Definition: game_config.cpp:76
int kill_experience
Definition: game_config.cpp:44
std::string unit_rgb
unsigned int tile_size
Definition: game_config.cpp:55
bool show_ally_orb
bool allow_insecure
Definition: game_config.cpp:78
bool show_disengaged_orb
void add_color_info(const game_config_view &v)
bool show_partial_orb
std::chrono::milliseconds lobby_refresh
Definition: game_config.cpp:72
std::string title_music
std::chrono::milliseconds lobby_network_timer
Definition: game_config.cpp:71
std::string default_terrain
Definition: game_config.cpp:57
bool show_enemy_orb
bool show_unmoved_orb
std::string reach_map_prefix
Definition: game_config.cpp:58
std::vector< std::string > default_victory_music
int combat_experience
Definition: game_config.cpp:45
void reset_color_info()
const std::string revision
void set_debug(bool new_debug)
Definition: game_config.cpp:97
std::vector< color_t > red_green_scale
bool exit_at_end
Definition: game_config.cpp:91
std::vector< color_t > blue_white_scale_text
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:66
int village_support
Definition: game_config.cpp:42
bool get_log_domain_severity(const std::string &name, severity &severity)
Definition: log.cpp:371
severity
Definition: log.hpp:83
bool set_log_domain_severity(const std::string &name, severity severity)
Definition: log.cpp:347
constexpr auto transform
Definition: ranges.hpp:41
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