The Battle for Wesnoth  1.17.17+dev
achievements.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2023
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 "achievements.hpp"
17 
18 #include "filesystem.hpp"
19 #include "game_config.hpp"
20 #include "log.hpp"
21 #include "preferences/general.hpp"
22 #include "serialization/parser.hpp"
24 
25 static lg::log_domain log_config("config");
26 #define ERR_CONFIG LOG_STREAM(err, log_config)
27 
28 sub_achievement::sub_achievement(const config& cfg, bool achieved)
29  : id_(cfg["id"].str())
30  , description_(cfg["description"].t_str())
31  , icon_(cfg["icon"].str()+"~GS()")
32  , icon_completed_(cfg["icon"].str())
33  , achieved_(achieved)
34 {}
35 
36 achievement::achievement(const config& cfg, const std::string& content_for, bool achieved, int progress)
37  : id_(cfg["id"].str())
38  , name_(cfg["name"].t_str())
39  , name_completed_(cfg["name_completed"].t_str())
40  , description_(cfg["description"].t_str())
41  , description_completed_(cfg["description_completed"].t_str())
42  , icon_(cfg["icon"].str()+"~GS()")
43  , icon_completed_(cfg["icon_completed"].str())
44  , hidden_(cfg["hidden"].to_bool())
45  , achieved_(achieved)
46  , max_progress_(cfg["max_progress"].to_int(0))
47  , current_progress_(progress)
48  , sound_path_(cfg["sound"].str())
49  , sub_achievements_()
50 {
51  if(name_completed_.empty()) {
53  }
56  }
57  if(icon_completed_.empty()) {
58  // avoid the ~GS() appended to icon_
59  icon_completed_ = cfg["icon"].str();
60  }
61 
62  for(const config& sub_ach : cfg.child_range("sub_achievement"))
63  {
64  std::string sub_id = sub_ach["id"].str();
65 
66  if(sub_id.empty()) {
67  ERR_CONFIG << "Achievement " << id_ << " has a sub-achievement missing the id attribute:\n" << sub_ach.debug();
68  } else {
69  sub_achievements_.emplace_back(sub_ach, achieved_ || preferences::sub_achievement(content_for, id_, sub_id));
70  max_progress_++;
71  }
72  }
73 }
74 
76  : display_name_(cfg["display_name"].t_str())
77  , content_for_(cfg["content_for"].str())
78  , achievements_()
79 {
80  for(const config& ach : cfg.child_range("achievement")) {
81  std::string id = ach["id"].str();
82 
83  if(id.empty()) {
84  ERR_CONFIG << content_for_ + " achievement missing id attribute:\n" << ach.debug();
85  } else if(id.find(',') != std::string::npos) {
86  ERR_CONFIG << content_for_ + " achievement id " << id << " contains a comma, skipping.";
87  continue;
88  } else {
90  }
91  }
92 }
93 
95  : achievement_list_()
96 {
97  reload();
98 }
99 
100 /**
101  * Reads the mainline achievements.cfg and then all the achievements of each installed add-on.
102  *
103  * This is intentionally handled separately from other WML loading so that:
104  * a) All achievements and their status are able to be displayed on the main menu right after Wesnoth starts and regardless of which add-ons are active.
105  * b) Add-ons can add additional achievements to other content, whether UMC or mainline. For example, a modification that adds more achievements for mainline campaigns.
106  *
107  * NOTE: These are *not* in any way related to Steam achievements!
108  */
110 {
111  achievement_list_.clear();
112  // mainline
113  try {
114  config cfg = read_achievements_file(game_config::path + "/data/achievements.cfg");
115  process_achievements_file(cfg, "Mainline");
116  } catch(const game::error& e) {
117  ERR_CONFIG << "Error processing mainline achievements, ignoring: " << e.what();
118  }
119 
120  // add-ons
121  std::vector<std::string> dirs;
123  for(const std::string& dir : dirs) {
124  try {
125  config cfg = read_achievements_file(filesystem::get_addons_dir() + "/" + dir + "/achievements.cfg");
126  process_achievements_file(cfg, dir);
127  } catch(const game::error& e) {
128  ERR_CONFIG << "Error processing add-on " << dir << " achievements, ignoring: " << e.what();
129  }
130  }
131 }
132 
133 /**
134  * Reads an achievements.cfg file into a config.
135  *
136  * @param path The path to the achievements.cfg file.
137  * @return The config containing all the achievements.
138  */
140 {
141  config cfg;
144  read(cfg, *stream);
145  }
146  return cfg;
147 }
148 
149 /**
150  * Processes a config object to add new achievements to @a achievement_list_.
151  *
152  * @param cfg The config containing additional achievements.
153  * @param content_source The source of the additional achievements - either mainline or an add-on.
154  */
155 void achievements::process_achievements_file(const config& cfg, const std::string& content_source)
156 {
157  for(const config& achgrp : cfg.child_range("achievement_group")) {
158  if(achgrp["content_for"].str().empty()) {
159  ERR_CONFIG << content_source + " achievement_group missing content_for attribute:\n" << achgrp.debug();
160  continue;
161  }
162  achievement_list_.emplace_back(achgrp);
163  }
164 }
#define ERR_CONFIG
static lg::log_domain log_config("config")
void process_achievements_file(const config &cfg, const std::string &content_source)
Processes a config object to add new achievements to achievement_list_.
std::vector< achievement_group > achievement_list_
config read_achievements_file(const std::string &path)
Reads an achievements.cfg file into a config.
void reload()
Reads the mainline achievements.cfg and then all the achievements of each installed add-on.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:161
child_itors child_range(config_key_type key)
Definition: config.cpp:277
bool empty() const
Definition: tstring.hpp:187
Declarations for File-IO.
Standard logging facilities (interface).
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs, name_mode mode, filter_mode filter, reorder_mode reorder, file_tree_checksum *checksum)
Get a list of all files and/or directories in a given directory.
Definition: filesystem.cpp:406
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:320
std::unique_ptr< std::istream > scoped_istream
Definition: filesystem.hpp:51
std::string get_addons_dir()
std::string path
Definition: filesystem.cpp:83
int progress_achievement(const std::string &content_for, const std::string &id, int limit, int max_progress, int amount)
Increments the achievement's current progress by amount if it hasn't already been completed.
Definition: general.cpp:1071
bool achievement(const std::string &content_for, const std::string &id)
Definition: general.cpp:1029
bool sub_achievement(const std::string &content_for, const std::string &id, const std::string &sub_id)
Definition: general.cpp:1132
filesystem::scoped_istream preprocess_file(const std::string &fname, preproc_map *defines)
Function to use the WML preprocessor on a file.
void read(config &cfg, std::istream &in, abstract_validator *validator)
Definition: parser.cpp:627
std::string content_for_
The internal ID used for this content.
std::vector< achievement > achievements_
The achievements associated to this content.
achievement_group(const config &cfg)
std::string icon_completed_
The icon of the achievement to show on the UI if the achievement is completed.
t_string name_completed_
The name of the achievement to show on the UI if the achievement is completed.
achievement(const config &cfg, const std::string &content_for, bool achieved, int progress)
t_string description_completed_
The name of the achievement to show on the UI if the achievement is completed.
t_string name_
The name of the achievement to show on the UI.
bool achieved_
Whether the achievement has been completed.
std::string id_
The ID of the achievement.
int max_progress_
When the achievement's current progress matches or equals this value, then it should be marked as com...
t_string description_
The description of the achievement to show on the UI.
std::vector< sub_achievement > sub_achievements_
The list of distinct sub-achievements for this achievement.
Base class for all the errors encountered by the engine.
Definition: exceptions.hpp:29
sub_achievement(const config &cfg, bool achieved)
#define e