-
Notifications
You must be signed in to change notification settings - Fork 164
/
Meter.h
143 lines (111 loc) · 5.41 KB
/
Meter.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
#ifndef _Meter_h_
#define _Meter_h_
#if __has_include(<charconv>)
#include <charconv>
#endif
#include <array>
#include <compare>
#include <cstdint>
#include <string>
#include <boost/serialization/access.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/version.hpp>
#include "../util/Export.h"
/** A Meter is a value and associated initial value that is used to track information
* about gamestate. A typical example is the population meter of a planet. */
class FO_COMMON_API Meter {
private:
static constexpr float FLOAT_INT_SCALE = 1000.0f;
static constexpr bool from_float_noexcept = noexcept(noexcept(
static_cast<int32_t>(float{} * FLOAT_INT_SCALE + (float{} > 0 ? 0.5f : -0.5f))));
static constexpr bool from_int_noexcept = noexcept(noexcept(int32_t{} / FLOAT_INT_SCALE));
public:
constexpr Meter() = default;
constexpr explicit Meter(float v) noexcept(from_float_noexcept) :
cur(FromFloat(v)),
init(FromFloat(v))
{};
constexpr Meter(float c, float i) noexcept(from_float_noexcept) :
cur(FromFloat(c)),
init(FromFloat(i))
{}
[[nodiscard]] constexpr float Current() const noexcept(from_int_noexcept) { return FromInt(cur); };
[[nodiscard]] constexpr float Initial() const noexcept(from_int_noexcept) { return FromInt(init); };
[[nodiscard]] constexpr auto operator<=>(const Meter&) const noexcept = default;
constexpr void SetCurrent(float current_value) noexcept(from_float_noexcept)
{ cur = FromFloat(current_value); }
constexpr void Set(float current_value, float initial_value) noexcept(from_float_noexcept) {
cur = FromFloat(current_value);
init = FromFloat(initial_value);
}
constexpr void ResetCurrent() noexcept
{ cur = DEFAULT_INT; } // initial unchanged
constexpr void Reset() noexcept {
cur = DEFAULT_INT;
init = DEFAULT_INT;
}
constexpr void AddToCurrent(float adjustment) noexcept(from_float_noexcept)
{ cur += FromFloat(adjustment); }
// no noexcept, even though could be in MSVC, due to issues with definitions
// of std::max and std::max causing problems with referring to them in this
// header, which means they probably can't be tested with noexcept(...) here
void ClampCurrentToRange(float min = DEFAULT_VALUE, float max = LARGE_VALUE); ///< ensures the current value falls in the range [\a min, \a max]
constexpr void BackPropagate() noexcept { init = cur; }
private:
static constexpr bool have_noexcept_to_chars =
#if defined(__cpp_lib_to_chars)
noexcept(std::from_chars(static_cast<const char*>(nullptr),
static_cast<const char*>(nullptr),
std::declval<int&>()));
#else
false;
#endif
public:
static constexpr bool dump_noexcept = have_noexcept_to_chars && from_int_noexcept;
///< returns text of meter values
[[nodiscard]] std::array<std::string::value_type, 64> Dump(uint8_t ntabs = 0) const noexcept(dump_noexcept);
int ToChars(char* buffer, char* const buffer_end) const noexcept(have_noexcept_to_chars);
using ToCharsArrayT = std::array<std::string::value_type, 24>;
[[nodiscard]] ToCharsArrayT ToChars() const noexcept(have_noexcept_to_chars);
int SetFromChars(std::string_view chars) noexcept(have_noexcept_to_chars);
static constexpr float DEFAULT_VALUE = 0.0f; ///< value assigned to current or initial when resetting or when no value is specified in a constructor
static constexpr float LARGE_VALUE = static_cast<float>(2 << 15); ///< a very large number, which is useful to set current to when it will be later clamped, to ensure that the result is the max value in the clamp range
static constexpr float INVALID_VALUE = -LARGE_VALUE; ///< sentinel value to indicate no valid value for this meter
private:
// Value must be rounded, otherwise a calculated increase of 0.99999997 will be truncated to 0.999.
// Negative values are increased by truncation, so the offset must be negative, too.
static constexpr int32_t FromFloat(float f) noexcept(from_float_noexcept)
{ return static_cast<int32_t>(f * FLOAT_INT_SCALE + (f > 0 ? 0.5f : -0.5f)); }
static constexpr float FromInt(int32_t i) noexcept(from_int_noexcept)
{ return i / FLOAT_INT_SCALE; }
static constexpr auto DEFAULT_INT = 0; // should be equal to Meter::FromFloat(DEFAULT_VALUE);
int32_t cur = DEFAULT_INT;
int32_t init = DEFAULT_INT;
friend class boost::serialization::access;
template <typename Archive>
void serialize(Archive& ar, const unsigned int version);
};
BOOST_CLASS_VERSION(Meter, 2)
template <typename Archive>
void Meter::serialize(Archive& ar, const unsigned int version)
{
if constexpr (Archive::is_loading::value) {
if (version < 2) {
float c = 0.0f, i = 0.0f;
ar & boost::serialization::make_nvp("c", c)
& boost::serialization::make_nvp("i", i);
cur = FromFloat(c);
init = FromFloat(i);
return;
}
}
ar & boost::serialization::make_nvp("c", cur)
& boost::serialization::make_nvp("i", init);
}
namespace boost::archive {
class xml_iarchive;
class xml_oarchive;
}
template<> void Meter::serialize(boost::archive::xml_iarchive&, const unsigned int version);
template<> void Meter::serialize(boost::archive::xml_oarchive&, const unsigned int version);
#endif