OpenTTD Source 20250613-master-ga1786fa1f4
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_type.h"
14#include "newgrf_spritegroup.h"
15#include "stringfilter_type.h"
16#include "strings_func.h"
18
19#include "table/strings.h"
20
21#include "safeguards.h"
22
24static constexpr char BADGE_CLASS_SEPARATOR = '/';
25
27class Badges {
28public:
29 std::vector<BadgeID> classes;
30 std::vector<Badge> specs;
31};
32
34static Badges _badges = {};
35
40std::span<const Badge> GetBadges()
41{
42 return _badges.specs;
43}
44
49std::span<const BadgeID> GetClassBadges()
50{
51 return _badges.classes;
52}
53
60{
61 auto it = std::ranges::find(_badges.classes, index);
62 if (it == std::end(_badges.classes)) {
63 it = _badges.classes.emplace(it, index);
64 }
65
66 return static_cast<BadgeClassID>(std::distance(std::begin(_badges.classes), it));
67}
68
73{
74 _badges = {};
75}
76
82Badge &GetOrCreateBadge(std::string_view label)
83{
84 /* Check if the label exists. */
85 auto it = std::ranges::find(_badges.specs, label, &Badge::label);
86 if (it != std::end(_badges.specs)) return *it;
87
88 BadgeClassID class_index;
89
90 /* Extract class. */
91 auto sep = label.find_first_of(BADGE_CLASS_SEPARATOR);
92 if (sep != std::string_view::npos) {
93 /* There is a separator, find (and create if necessary) the class label. */
94 class_index = GetOrCreateBadge(label.substr(0, sep)).class_index;
95 it = std::end(_badges.specs);
96 }
97
98 BadgeID index = BadgeID(std::distance(std::begin(_badges.specs), it));
99 if (sep == std::string_view::npos) {
100 /* There is no separator, so this badge is a class badge. */
101 class_index = GetOrCreateBadgeClass(index);
102 }
103
104 it = _badges.specs.emplace(it, label, index, class_index);
105 return *it;
106}
107
114{
115 if (index.base() >= std::size(_badges.specs)) return nullptr;
116 return &_badges.specs[index.base()];
117}
118
124Badge *GetBadgeByLabel(std::string_view label)
125{
126 auto it = std::ranges::find(_badges.specs, label, &Badge::label);
127 if (it == std::end(_badges.specs)) return nullptr;
128
129 return &*it;
130}
131
138{
139 if (class_index.base() >= std::size(_badges.classes)) return nullptr;
140 return GetBadge(_badges.classes[class_index.base()]);
141}
142
145 const Badge &badge;
146 const std::optional<TimerGameCalendar::Date> introduction_date;
147
154 BadgeScopeResolver(ResolverObject &ro, const Badge &badge, const std::optional<TimerGameCalendar::Date> introduction_date)
155 : ScopeResolver(ro), badge(badge), introduction_date(introduction_date) { }
156
157 uint32_t GetVariable(uint8_t variable, [[maybe_unused]] uint32_t parameter, bool &available) const override;
158};
159
160/* virtual */ uint32_t BadgeScopeResolver::GetVariable(uint8_t variable, [[maybe_unused]] uint32_t parameter, bool &available) const
161{
162 switch (variable) {
163 case 0x40:
164 if (this->introduction_date.has_value()) return this->introduction_date->base();
165 return TimerGameCalendar::date.base();
166
167 default: break;
168 }
169
170 available = false;
171 return UINT_MAX;
172}
173
176 BadgeScopeResolver self_scope;
177
178 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);
179
180 ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, uint8_t relative = 0) override
181 {
182 switch (scope) {
183 case VSG_SCOPE_SELF: return &this->self_scope;
184 default: return ResolverObject::GetScope(scope, relative);
185 }
186 }
187
188 GrfSpecFeature GetFeature() const override;
189 uint32_t GetDebugID() const override;
190};
191
193{
194 return GSF_BADGES;
195}
196
198{
199 return this->self_scope.badge.index.base();
200}
201
211BadgeResolverObject::BadgeResolverObject(const Badge &badge, GrfSpecFeature feature, std::optional<TimerGameCalendar::Date> introduction_date, CallbackID callback, uint32_t callback_param1, uint32_t callback_param2)
212 : ResolverObject(badge.grf_prop.grffile, callback, callback_param1, callback_param2), self_scope(*this, badge, introduction_date)
213{
214 assert(feature <= GSF_END);
215 this->root_spritegroup = this->self_scope.badge.grf_prop.GetFirstSpriteGroupOf({feature, GSF_DEFAULT});
216}
217
225uint32_t GetBadgeVariableResult(const GRFFile &grffile, std::span<const BadgeID> badges, uint32_t parameter)
226{
227 if (parameter >= std::size(grffile.badge_list)) return UINT_MAX;
228
229 BadgeID index = grffile.badge_list[parameter];
230 return std::ranges::find(badges, index) != std::end(badges);
231}
232
237{
238 Badge *b = GetBadge(index);
239 assert(b != nullptr);
240 b->features.Set(feature);
241}
242
250void AppendCopyableBadgeList(std::vector<BadgeID> &dst, std::span<const BadgeID> src, GrfSpecFeature feature)
251{
252 for (const BadgeID &index : src) {
253 /* Is badge already present? */
254 if (std::ranges::find(dst, index) != std::end(dst)) continue;
255
256 /* Is badge copyable? */
257 Badge *badge = GetBadge(index);
258 if (badge == nullptr) continue;
259 if (!badge->flags.Test(BadgeFlag::Copy)) continue;
260
261 dst.push_back(index);
262 badge->features.Set(feature);
263 }
264}
265
268{
269 for (const Badge &badge : GetBadges()) {
270 Badge *class_badge = GetClassBadge(badge.class_index);
271 assert(class_badge != nullptr);
272 class_badge->features.Set(badge.features);
273 if (badge.name != STR_NULL) class_badge->flags.Set(BadgeFlag::HasText);
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
300UsedBadgeClasses::UsedBadgeClasses(GrfSpecFeature feature) : feature(feature)
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}
344
350bool BadgeDropdownFilter::Filter(std::span<const BadgeID> badges) const
351{
352 if (this->badges.empty()) return true;
353
354 /* We want all filtered badges to match. */
355 return std::ranges::all_of(this->badges, [&badges](const auto &badge) { return std::ranges::find(badges, badge.second) != std::end(badges); });
356}
bool Filter(std::span< const BadgeID > badges) const
Test if the given badges matches the filtered badge list.
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.
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.
Types related to NewGRF badges.
@ UseCompanyColour
Apply company colour palette to this badge.
@ Copy
Copy badge to related things.
@ HasText
Internal flag set if the badge has text.
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.