The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
achievements.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2003 - 2025
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 "log.hpp"
23
24static lg::log_domain log_config("config");
25#define ERR_CONFIG LOG_STREAM(err, log_config)
26
28 : id_(cfg["id"].str())
29 , description_(cfg["description"].t_str())
30 , icon_(cfg["icon"].str()+"~GS()")
31 , icon_completed_(cfg["icon"].str())
32 , achieved_(achieved)
33{}
34
35achievement::achievement(const config& cfg, const std::string& content_for, bool achieved, int progress)
36 : id_(cfg["id"].str())
37 , name_(cfg["name"].t_str())
38 , name_completed_(cfg["name_completed"].t_str())
39 , description_(cfg["description"].t_str())
40 , description_completed_(cfg["description_completed"].t_str())
41 , icon_(cfg["icon"].str()+"~GS()")
42 , icon_completed_(cfg["icon_completed"].str())
43 , hidden_(cfg["hidden"].to_bool())
44 , achieved_(achieved)
45 , max_progress_(cfg["max_progress"].to_int(0))
46 , current_progress_(progress)
47 , sound_path_(cfg["sound"].str())
48 , sub_achievements_()
49{
52 }
55 }
56 if(icon_completed_.empty()) {
57 // avoid the ~GS() appended to icon_
58 icon_completed_ = cfg["icon"].str();
59 }
60
61 for(const config& sub_ach : cfg.child_range("sub_achievement"))
62 {
63 std::string sub_id = sub_ach["id"].str();
64
65 if(sub_id.empty()) {
66 ERR_CONFIG << "Achievement " << id_ << " has a sub-achievement missing the id attribute:\n" << sub_ach.debug();
67 } else {
68 sub_achievements_.emplace_back(sub_ach, achieved_ || prefs::get().sub_achievement(content_for, id_, sub_id));
70 }
71 }
72}
73
75 : display_name_(cfg["display_name"].t_str())
76 , content_for_(cfg["content_for"].str())
77 , achievements_()
78{
79 for(const config& ach : cfg.child_range("achievement")) {
80 std::string id = ach["id"].str();
81
82 if(id.empty()) {
83 ERR_CONFIG << content_for_ + " achievement missing id attribute:\n" << ach.debug();
84 } else if(id.find(',') != std::string::npos) {
85 ERR_CONFIG << content_for_ + " achievement id " << id << " contains a comma, skipping.";
86 continue;
87 } else {
89 }
90 }
91}
92
94 : achievement_list_()
95{
96 reload();
97}
98
99/**
100 * Reads the mainline achievements.cfg and then all the achievements of each installed add-on.
101 *
102 * This is intentionally handled separately from other WML loading so that:
103 * 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.
104 * 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.
105 *
106 * NOTE: These are *not* in any way related to Steam achievements!
107 */
109{
110 achievement_list_.clear();
111 // mainline
112 try {
113 config cfg = read_achievements_file(game_config::path + "/data/achievements.cfg");
114 process_achievements_file(cfg, "Mainline");
115 } catch(const game::error& e) {
116 ERR_CONFIG << "Error processing mainline achievements, ignoring: " << e.what();
117 }
118
119 // add-ons
120 std::vector<std::string> dirs;
122 for(const std::string& dir : dirs) {
123 try {
124 config cfg = read_achievements_file(filesystem::get_addons_dir() + "/" + dir + "/achievements.cfg");
126 } catch(const game::error& e) {
127 ERR_CONFIG << "Error processing add-on " << dir << " achievements, ignoring: " << e.what();
128 }
129 }
130}
131
132/**
133 * Reads an achievements.cfg file into a config.
134 *
135 * @param path The path to the achievements.cfg file.
136 * @return The config containing all the achievements.
137 */
139{
140 if(filesystem::file_exists(path)) {
141 return io::read(*preprocess_file(path));
142 } else {
143 return {};
144 }
145}
146
147/**
148 * Processes a config object to add new achievements to @a achievement_list_.
149 *
150 * @param cfg The config containing additional achievements.
151 * @param content_source The source of the additional achievements - either mainline or an add-on.
152 */
153void achievements::process_achievements_file(const config& cfg, const std::string& content_source)
154{
155 for(const config& achgrp : cfg.child_range("achievement_group")) {
156 if(achgrp["content_for"].str().empty()) {
157 ERR_CONFIG << content_source + " achievement_group missing content_for attribute:\n" << achgrp.debug();
158 continue;
159 }
160 achievement_list_.emplace_back(achgrp);
161 }
162}
#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:157
child_itors child_range(std::string_view key)
Definition config.cpp:268
std::string debug() const
Definition config.cpp:1214
static prefs & get()
bool achievement(const std::string &content_for, const std::string &id)
bool sub_achievement(const std::string &content_for, const std::string &id, const std::string &sub_id)
int progress_achievement(const std::string &content_for, const std::string &id, int limit=999999, int max_progress=999999, int amount=0)
Increments the achievement's current progress by amount if it hasn't already been completed.
bool empty() const
Definition tstring.hpp:200
const config * cfg
Declarations for File-IO.
Standard logging facilities (interface).
bool file_exists(const std::string &name)
Returns true if a file or directory with such name already exists.
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs=nullptr, name_mode mode=name_mode::FILE_NAME_ONLY, filter_mode filter=filter_mode::NO_FILTER, reorder_mode reorder=reorder_mode::DONT_REORDER, file_tree_checksum *checksum=nullptr)
Get a list of all files and/or directories in a given directory.
std::string get_addons_dir()
std::string path
config read(std::istream &in, abstract_validator *validator)
Definition parser.cpp:610
filesystem::scoped_istream preprocess_file(const std::string &fname, preproc_map &defines)
Function to use the WML preprocessor on a file.
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.
sub_achievement(const config &cfg, bool achieved)
#define e