OpenTTD Source 20250529-master-g10c159a79f
newgrf_badge.cpp
Go to the documentation of this file.
1 /*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
10#include "stdafx.h"
11#include "newgrf.h"
12#include "newgrf_badge.h"
13#include "newgrf_badge_config.h"
14#include "newgrf_badge_type.h"
15#include "newgrf_spritegroup.h"
16#include "stringfilter_type.h"
17#include "strings_func.h"
19
20#include "table/strings.h"
21
22#include "safeguards.h"
23
25static constexpr char BADGE_CLASS_SEPARATOR = '/';
26
28class Badges {
29public:
30 std::vector<BadgeID> classes;
31 std::vector<Badge> specs;
32};
33
35static Badges _badges = {};
36
41std::span<const Badge> GetBadges()
42{
43 return _badges.specs;
44}
45
50std::span<const BadgeID> GetClassBadges()
51{
52 return _badges.classes;
53}
54
61{
62 auto it = std::ranges::find(_badges.classes, index);
63 if (it == std::end(_badges.classes)) {
64 it = _badges.classes.emplace(it, index);
65 }
66
67 return static_cast<BadgeClassID>(std::distance(std::begin(_badges.classes), it));
68}
69
74{
75 _badges = {};
76}
77
83Badge &GetOrCreateBadge(std::string_view label)
84{
85 /* Check if the label exists. */
86 auto it = std::ranges::find(_badges.specs, label, &Badge::label);
87 if (it != std::end(_badges.specs)) return *it;
88
89 BadgeClassID class_index;
90
91 /* Extract class. */
92 auto sep = label.find_first_of(BADGE_CLASS_SEPARATOR);
93 if (sep != std::string_view::npos) {
94 /* There is a separator, find (and create if necessary) the class label. */
95 class_index = GetOrCreateBadge(label.substr(0, sep)).class_index;
96 it = std::end(_badges.specs);
97 }
98
99 BadgeID index = BadgeID(std::distance(std::begin(_badges.specs), it));
100 if (sep == std::string_view::npos) {
101 /* There is no separator, so this badge is a class badge. */
102 class_index = GetOrCreateBadgeClass(index);
103 }
104
105 it = _badges.specs.emplace(it, label, index, class_index);
106 return *it;
107}
108
115{
116 if (index.base() >= std::size(_badges.specs)) return nullptr;
117 return &_badges.specs[index.base()];
118}
119
125Badge *GetBadgeByLabel(std::string_view label)
126{
127 auto it = std::ranges::find(_badges.specs, label, &Badge::label);
128 if (it == std::end(_badges.specs)) return nullptr;
129
130 return &*it;
131}
132
139{
140 if (class_index.base() >= std::size(_badges.classes)) return nullptr;
141 return GetBadge(_badges.classes[class_index.base()]);
142}
143
146 const Badge &badge;
147 const std::optional<TimerGameCalendar::Date> introduction_date;
148
155 BadgeScopeResolver(ResolverObject &ro, const Badge &badge, const std::optional<TimerGameCalendar::Date> introduction_date)
156 : ScopeResolver(ro), badge(badge), introduction_date(introduction_date) { }
157
158 uint32_t GetVariable(uint8_t variable, [[maybe_unused]] uint32_t parameter, bool &available) const override;
159};
160
161/* virtual */ uint32_t BadgeScopeResolver::GetVariable(uint8_t variable, [[maybe_unused]] uint32_t parameter, bool &available) const
162{
163 switch (variable) {
164 case 0x40:
165 if (this->introduction_date.has_value()) return this->introduction_date->base();
166 return TimerGameCalendar::date.base();
167
168 default: break;
169 }
170
171 available = false;
172 return UINT_MAX;
173}
174
177 BadgeScopeResolver self_scope;
178
179 BadgeResolverObject(const Badge &badge, GrfSpecFeature feature, std::optional<TimerGameCalendar::Date> introduction_date, CallbackID callback = CBID_NO_CALLBACK, uint32_t callback_param1 = 0, uint32_t callback_param2 = 0);
180
181 ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, uint8_t relative = 0) override
182 {
183 switch (scope) {
184 case VSG_SCOPE_SELF: return &this->self_scope;
185 default: return ResolverObject::GetScope(scope, relative);
186 }
187 }
188
189 GrfSpecFeature GetFeature() const override;
190 uint32_t GetDebugID() const override;
191};
192
194{
195 return GSF_BADGES;
196}
197
199{
200 return this->self_scope.badge.index.base();
201}
202
212BadgeResolverObject::BadgeResolverObject(const Badge &badge, GrfSpecFeature feature, std::optional<TimerGameCalendar::Date> introduction_date, CallbackID callback, uint32_t callback_param1, uint32_t callback_param2)
213 : ResolverObject(badge.grf_prop.grffile, callback, callback_param1, callback_param2), self_scope(*this, badge, introduction_date)
214{
215 assert(feature <= GSF_END);
216 this->root_spritegroup = this->self_scope.badge.grf_prop.GetFirstSpriteGroupOf({feature, GSF_DEFAULT});
217}
218
226uint32_t GetBadgeVariableResult(const GRFFile &grffile, std::span<const BadgeID> badges, uint32_t parameter)
227{
228 if (parameter >= std::size(grffile.badge_list)) return UINT_MAX;
229
230 BadgeID index = grffile.badge_list[parameter];
231 return std::ranges::find(badges, index) != std::end(badges);
232}
233
238{
239 Badge *b = GetBadge(index);
240 assert(b != nullptr);
241 b->features.Set(feature);
242}
243
251void AppendCopyableBadgeList(std::vector<BadgeID> &dst, std::span<const BadgeID> src, GrfSpecFeature feature)
252{
253 for (const BadgeID &index : src) {
254 /* Is badge already present? */
255 if (std::ranges::find(dst, index) != std::end(dst)) continue;
256
257 /* Is badge copyable? */
258 Badge *badge = GetBadge(index);
259 if (badge == nullptr) continue;
260 if (!badge->flags.Test(BadgeFlag::Copy)) continue;
261
262 dst.push_back(index);
263 badge->features.Set(feature);
264 }
265}
266
269{
270 for (const Badge &badge : GetBadges()) {
271 Badge *class_badge = GetClassBadge(badge.class_index);
272 assert(class_badge != nullptr);
273 class_badge->features.Set(badge.features);
274 }
275}
276
285PalSpriteID GetBadgeSprite(const Badge &badge, GrfSpecFeature feature, std::optional<TimerGameCalendar::Date> introduction_date, PaletteID remap)
286{
287 BadgeResolverObject object(badge, feature, introduction_date);
288 const auto *group = object.Resolve<ResultSpriteGroup>();
289 if (group == nullptr || group->num_sprites == 0) return {0, PAL_NONE};
290
291 PaletteID pal = badge.flags.Test(BadgeFlag::UseCompanyColour) ? remap : PAL_NONE;
292
293 return {group->sprite, pal};
294}
295
301{
302 for (auto index : _badges.classes) {
303 Badge *class_badge = GetBadge(index);
304 if (!class_badge->features.Test(feature)) continue;
305
306 this->classes.push_back(class_badge->class_index);
307 }
308
309 std::ranges::sort(this->classes, [](const BadgeClassID &a, const BadgeClassID &b) { return GetClassBadge(a)->label < GetClassBadge(b)->label; });
310}
311
318{
319 /* Do not filter if the filter text box is empty */
320 if (filter.IsEmpty()) return;
321
322 /* Pre-build list of badges that match by string. */
323 for (const auto &badge : GetBadges()) {
324 if (badge.name == STR_NULL) continue;
325 if (!badge.features.Test(feature)) continue;
326
327 filter.ResetState();
328 filter.AddLine(GetString(badge.name));
329 if (!filter.GetState()) continue;
330
331 this->badges.insert(badge.index);
332 }
333}
334
340bool BadgeTextFilter::Filter(std::span<const BadgeID> badges) const
341{
342 return std::ranges::any_of(badges, [this](const BadgeID &badge) { return std::ranges::binary_search(this->badges, badge); });
343}
bool Filter(std::span< const BadgeID > badges) const
Test if any of the given badges matches the filtered badge list.
BadgeTextFilter(struct StringFilter &filter, GrfSpecFeature feature)
Construct a badge text filter.
BadgeClassID class_index
Index of class this badge belongs to.
VariableGRFFileProps< GrfSpecFeature > grf_prop
Sprite information.
std::string label
Label of badge.
BadgeID index
Index assigned to badge.
BadgeFlags flags
Display flags.
GrfSpecFeatures features
Bitmask of which features use this badge.
Global state for badge definitions.
std::vector< BadgeID > classes
List of known badge classes.
std::vector< Badge > specs
List of known badges.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Set()
Set all bits.
static Date date
Current date in days (day counter).
std::vector< BadgeClassID > classes
List of badge classes.
UsedBadgeClasses(GrfSpecFeature feature)
Create a list of used badge classes for a feature.
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:18
Base for the NewGRF implementation.
GrfSpecFeature
Definition newgrf.h:69
@ GSF_DEFAULT
Unspecified feature, default badge.
Definition newgrf.h:94
static Badges _badges
Static instance of badge state.
Badge * GetBadge(BadgeID index)
Get a badge if it exists.
std::span< const Badge > GetBadges()
Get a read-only view of badges.
void ApplyBadgeFeaturesToClassBadges()
Apply features from all badges to their badge classes.
void MarkBadgeSeen(BadgeID index, GrfSpecFeature feature)
Mark a badge a seen (used) by a feature.
void AppendCopyableBadgeList(std::vector< BadgeID > &dst, std::span< const BadgeID > src, GrfSpecFeature feature)
Append copyable badges from a list onto another.
PalSpriteID GetBadgeSprite(const Badge &badge, GrfSpecFeature feature, std::optional< TimerGameCalendar::Date > introduction_date, PaletteID remap)
Get sprite for the given badge.
std::span< const BadgeID > GetClassBadges()
Get a read-only view of class badge index.
Badge * GetClassBadge(BadgeClassID class_index)
Get the badge class of a badge label.
static constexpr char BADGE_CLASS_SEPARATOR
Separator to identify badge classes from a label.
static BadgeClassID GetOrCreateBadgeClass(BadgeID index)
Assign a BadgeClassID to the given badge.
Badge & GetOrCreateBadge(std::string_view label)
Register a badge label and return its global index.
uint32_t GetBadgeVariableResult(const GRFFile &grffile, std::span< const BadgeID > badges, uint32_t parameter)
Test for a matching badge in a list of badges, returning the number of matching bits.
Badge * GetBadgeByLabel(std::string_view label)
Get a badge by label if it exists.
void ResetBadges()
Reset badges to the default state.
Functions related to NewGRF badges.
Badge * GetBadge(BadgeID index)
Get a badge if it exists.
std::span< const Badge > GetBadges()
Get a read-only view of badges.
Badge * GetClassBadge(BadgeClassID class_index)
Get the badge class of a badge label.
Functions related to NewGRF badge configuration.
Types related to NewGRF badges.
@ UseCompanyColour
Apply company colour palette to this badge.
@ Copy
Copy badge to related things.
CallbackID
List of implemented NewGRF callbacks.
@ CBID_NO_CALLBACK
Set when using the callback resolve system, but not to resolve a callback.
Action 2 handling.
VarSpriteGroupScope
@ VSG_SCOPE_SELF
Resolved object itself.
A number of safeguards to prevent using unsafe methods.
Definition of base types and functions in a cross-platform compatible way.
Searching and filtering using a stringterm.
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:415
Functions related to OTTD's strings.
Resolver of badges.
GrfSpecFeature GetFeature() const override
Get the feature number being resolved for.
BadgeResolverObject(const Badge &badge, GrfSpecFeature feature, std::optional< TimerGameCalendar::Date > introduction_date, CallbackID callback=CBID_NO_CALLBACK, uint32_t callback_param1=0, uint32_t callback_param2=0)
Constructor of the badge resolver.
ScopeResolver * GetScope(VarSpriteGroupScope scope=VSG_SCOPE_SELF, uint8_t relative=0) override
Get a resolver for the scope.
uint32_t GetDebugID() const override
Get an identifier for the item being resolved.
Resolver for a badge scope.
BadgeScopeResolver(ResolverObject &ro, const Badge &badge, const std::optional< TimerGameCalendar::Date > introduction_date)
Scope resolver of a badge.
uint32_t GetVariable(uint8_t variable, uint32_t parameter, bool &available) const override
Get a variable value.
Dynamic data of a loaded NewGRF.
Definition newgrf.h:115
std::vector< BadgeID > badge_list
Badge translation table (local index -> global index)
Definition newgrf.h:139
Combination of a palette sprite and a 'real' sprite.
Definition gfx_type.h:22
Interface for SpriteGroup-s to access the gamestate.
uint32_t callback_param2
Second parameter (var 18) of the callback.
CallbackID callback
Callback being resolved.
uint32_t callback_param1
First parameter (var 10) of the callback.
const SpriteGroup * root_spritegroup
Root SpriteGroup to use for resolving.
virtual ScopeResolver * GetScope(VarSpriteGroupScope scope=VSG_SCOPE_SELF, uint8_t relative=0)
Get a resolver for the scope.
Interface to query and set values specific to a single VarSpriteGroupScope (action 2 scope).
ResolverObject & ro
Surrounding resolver object.
String filter and state.
bool IsEmpty() const
Check whether any filter words were entered.
void ResetState()
Reset the matching state to process a new item.
bool GetState() const
Get the matching state of the current item.
const struct SpriteGroup * GetFirstSpriteGroupOf(std::initializer_list< Tkey > indices) const
Get the first existing SpriteGroup from a list of options.
Definition of the game-calendar-timer.