forked from CleverRaven/Cataclysm-DDA
-
Notifications
You must be signed in to change notification settings - Fork 0
/
achievement.h
243 lines (201 loc) · 7.63 KB
/
achievement.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
#ifndef CATA_SRC_ACHIEVEMENT_H
#define CATA_SRC_ACHIEVEMENT_H
#include <array>
#include <functional>
#include <iosfwd>
#include <memory>
#include <new>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "calendar.h"
#include "cata_variant.h"
#include "event_subscriber.h"
#include "optional.h"
#include "translations.h"
#include "type_id.h"
class JsonIn;
class JsonObject;
class JsonOut;
class achievements_tracker;
struct achievement_requirement;
template <typename E> struct enum_traits;
namespace cata
{
class event;
} // namespace cata
class requirement_watcher;
class stats_tracker;
enum class achievement_comparison : int {
equal,
less_equal,
greater_equal,
anything,
last,
};
template<>
struct enum_traits<achievement_comparison> {
static constexpr achievement_comparison last = achievement_comparison::last;
};
enum class achievement_completion : int {
pending,
completed,
failed,
last
};
template<>
struct enum_traits<achievement_completion> {
static constexpr achievement_completion last = achievement_completion::last;
};
class achievement
{
public:
achievement() = default;
void load( const JsonObject &, const std::string & );
void check() const;
static void load_achievement( const JsonObject &, const std::string & );
static void finalize();
static void check_consistency();
static const std::vector<achievement> &get_all();
static void reset();
achievement_id id;
std::vector<std::pair<achievement_id, mod_id>> src;
bool was_loaded = false;
const translation &name() const {
return name_;
}
const translation &description() const {
return description_;
}
bool is_conduct() const {
return is_conduct_;
}
const std::vector<achievement_id> &hidden_by() const {
return hidden_by_;
}
class time_bound
{
public:
enum class epoch : int {
cataclysm,
game_start,
last
};
void deserialize( const JsonObject &jo );
void check( const achievement_id & ) const;
time_point target() const;
achievement_completion completed() const;
bool becomes_false() const;
std::string ui_text( bool is_conduct ) const;
private:
achievement_comparison comparison_ = achievement_comparison::anything;
epoch epoch_ = epoch::cataclysm;
time_duration period_;
};
const cata::optional<time_bound> &time_constraint() const {
return time_constraint_;
}
const std::vector<achievement_requirement> &requirements() const {
return requirements_;
}
private:
translation name_;
translation description_;
bool is_conduct_ = false;
std::vector<achievement_id> hidden_by_;
cata::optional<time_bound> time_constraint_;
std::vector<achievement_requirement> requirements_;
};
template<>
struct enum_traits<achievement::time_bound::epoch> {
static constexpr achievement::time_bound::epoch last = achievement::time_bound::epoch::last;
};
// Once an achievement is either completed or failed it is stored as an
// achievement_state
struct achievement_state {
// The final state
achievement_completion completion;
// When it became that state
time_point last_state_change;
// The values for each requirement at the time of completion or failure
std::vector<cata_variant> final_values;
std::string ui_text( const achievement * ) const;
void serialize( JsonOut & ) const;
void deserialize( const JsonObject &jo );
};
class achievement_tracker
{
public:
// Non-movable because requirement_watcher stores a pointer to us
achievement_tracker( const achievement_tracker & ) = delete;
achievement_tracker &operator=( const achievement_tracker & ) = delete;
achievement_tracker( const achievement &a, achievements_tracker &tracker,
stats_tracker &stats );
void set_requirement( requirement_watcher *watcher, bool is_satisfied );
bool time_is_expired() const;
std::vector<cata_variant> current_values() const;
std::string ui_text() const;
private:
const achievement *achievement_;
achievements_tracker *tracker_;
std::vector<std::unique_ptr<requirement_watcher>> watchers_;
// sorted_watchers_ maintains two sets of watchers, categorised by
// whether they watch a satisfied or unsatisfied requirement. This
// allows us to check whether the achievement is met on each new stat
// value in O(1) time.
std::array<std::unordered_set<requirement_watcher *>, 2> sorted_watchers_;
};
class achievements_tracker : public event_subscriber
{
public:
// Non-movable because achievement_tracker stores a pointer to us
achievements_tracker( const achievements_tracker & ) = delete;
achievements_tracker &operator=( const achievements_tracker & ) = delete;
/**
* @param active Whether this achievements_tracker needs to create
* watchers for the stats_tracker to monitor ongoing events. If only
* using the achievements_tracker for analyzing past achievements, this
* should not be necessary.
*/
achievements_tracker(
stats_tracker &,
const std::function<void( const achievement *, bool )> &achievement_attained_callback,
const std::function<void( const achievement *, bool )> &achievement_failed_callback,
bool active );
~achievements_tracker() override;
// Return all scores which are valid now and existed at game start
std::vector<const achievement *> valid_achievements() const;
void report_achievement( const achievement *, achievement_completion );
achievement_completion is_completed( const achievement_id & ) const;
bool is_hidden( const achievement * ) const;
std::string ui_text_for( const achievement * ) const;
bool is_enabled() const {
return enabled_;
}
void set_enabled( bool enabled ) {
enabled_ = enabled;
}
void clear();
void notify( const cata::event & ) override;
void serialize( JsonOut & ) const;
void deserialize( const JsonObject &jo );
private:
void init_watchers();
stats_tracker *stats_ = nullptr; // NOLINT(cata-serialize)
bool enabled_ = true;
// Active is true when this is the 'real' achievements_tracker for an
// ongoing game, but false when it's being used to analyze data from a
// past game.
bool active_; // NOLINT(cata-serialize)
// NOLINTNEXTLINE(cata-serialize)
std::function<void( const achievement *, bool )> achievement_attained_callback_;
// NOLINTNEXTLINE(cata-serialize)
std::function<void( const achievement *, bool )> achievement_failed_callback_;
std::unordered_set<achievement_id> initial_achievements_;
// Class invariant: each valid achievement has exactly one of a watcher
// (if it's pending) or a status (if it's completed or failed).
std::unordered_map<achievement_id, achievement_tracker> trackers_; // NOLINT(cata-serialize)
std::unordered_map<achievement_id, achievement_state> achievements_status_;
};
achievements_tracker &get_achievements();
#endif // CATA_SRC_ACHIEVEMENT_H